From de62bfa662b566e598ab26de2ee48d3296b0bcf0 Mon Sep 17 00:00:00 2001 From: "Sameh M. Abdulah" Date: Thu, 23 Nov 2023 14:51:05 +0300 Subject: [PATCH 01/82] add the pow_exp kernel and tested --- CONTRIBUTING.md | 2 +- .../concrete/UnivariatePowExpStationary.hpp | 85 +++++++++++++++++++ src/kernels/CMakeLists.txt | 3 +- .../concrete/UnivariatePowExpStationary.cpp | 66 ++++++++++++++ 4 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 inst/include/kernels/concrete/UnivariatePowExpStationary.hpp create mode 100644 src/kernels/concrete/UnivariatePowExpStationary.cpp diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c8b28557..f1b5e5d9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -260,7 +260,7 @@ To add a new kernel, you need to follow these steps. 1. Place your kernel header file in the inst/include/kernels/concrete directory. The file name should match the kernel's name. For instance, if your header file is named UnivariateMaternStationary.hpp, it can be invoked using either univariate_matern_stationary or UnivariateMaternStationary. The naming linkage is handled automatically, so there's no additional setup required on your part. -2. Derive from the base class located in kernel.hpp and implement the necessary functions. +2. Derive from the base class located in Kernel.hpp and implement the necessary functions. 3. Ensure your kernel includes all the requisite functions that adhere to the established naming conventions found in other kernels. This will allow for proper support and integration of your new kernel. diff --git a/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp b/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp new file mode 100644 index 00000000..111e51ca --- /dev/null +++ b/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp @@ -0,0 +1,85 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file UnivariatePowExpStationary.hpp + * @brief Defines the UnivariatePowExpStationary class, a univariate stationary PowExp kernel. + * @version 1.0.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @author Suhas Shankar + * @author Mary Lai Salvana + * @date 2024-11-22 + * + * This file provides the declaration of the UnivariatePowExpStationary class, which is a subclass of the Kernel class + * and represents a univariate stationary PowExp kernel. It provides a method for generating a covariance matrix + * using a set of input locations and kernel parameters. + * +**/ + +#ifndef EXAGEOSTATCPP_UNIVARIATEMATERNSTATIONARY_HPP +#define EXAGEOSTATCPP_UNIVARIATEMATERNSTATIONARY_HPP + +#include + +namespace exageostat::kernels { + + /** + * @class UnivariatePowExpStationary + * @brief A class representing a Univariate PowExp Stationary kernel. + * @details This class represents a Univariate PowExp Stationary, which is a subclass of the Kernel class. + * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. + * + */ + template + class UnivariatePowExpStationary : public Kernel { + + public: + + /** + * @brief Constructs a new UnivariatePowExpStationary object. + * @details Initializes a new UnivariatePowExpStationary object with default values. + */ + UnivariatePowExpStationary(); + + /** + * @brief Virtual destructor to allow calls to the correct concrete destructor. + * + */ + ~UnivariatePowExpStationary() override = default; + + /** + * @brief Generates a covariance matrix using a set of locations and kernel parameters. + * @copydoc Kernel::GenerateCovarianceMatrix() + */ + void + GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, + const int &aColumnOffset, dataunits::Locations &aLocation1, + dataunits::Locations &aLocation2, dataunits::Locations &aLocation3, + T *apLocalTheta, const int &aDistanceMetric) override; + + /** + * @brief Creates a new UnivariatePowExpStationary object. + * @details This method creates a new UnivariatePowExpStationary object and returns a pointer to it. + * @return A pointer to the new UnivariatePowExpStationary object. + * + */ + static Kernel *Create(); + + private: + //// Used plugin name for static registration + static bool plugin_name; + }; + + /** + * @brief Instantiates the Data Generator class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(UnivariatePowExpStationary) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_UNIVARIATEMATERNSTATIONARY_HPP diff --git a/src/kernels/CMakeLists.txt b/src/kernels/CMakeLists.txt index 6b89f681..c41d6f6d 100644 --- a/src/kernels/CMakeLists.txt +++ b/src/kernels/CMakeLists.txt @@ -33,6 +33,7 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateExpNonGaussian.cpp ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TrivariateMaternParsimonious.cpp ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternNonStat.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariatePowExpStationary.cpp ${SOURCES} PARENT_SCOPE - ) \ No newline at end of file + ) diff --git a/src/kernels/concrete/UnivariatePowExpStationary.cpp b/src/kernels/concrete/UnivariatePowExpStationary.cpp new file mode 100644 index 00000000..ab7c7073 --- /dev/null +++ b/src/kernels/concrete/UnivariatePowExpStationary.cpp @@ -0,0 +1,66 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file UnivariatePowExpStationary.cpp + * @brief Implementation of the UnivariatePowExpStationary kernel. + * @version 1.0.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2023-04-14 +**/ + +#include +#include + +using namespace exageostat::kernels; +using namespace exageostat::dataunits; +using namespace exageostat::helpers; + +template +UnivariatePowExpStationary::UnivariatePowExpStationary() { + this->mP = 1; + this->mParametersNumber = 3; +} + +template +Kernel *UnivariatePowExpStationary::Create() { + KernelsConfigurations::GetParametersNumberKernelMap()["UnivariatePowExpStationary"] = 3; + return new UnivariatePowExpStationary(); +} + +namespace exageostat::kernels { + template bool UnivariatePowExpStationary::plugin_name = plugins::PluginRegistry>::Add( + "UnivariatePowExpStationary", UnivariatePowExpStationary::Create); +} + +template +void +UnivariatePowExpStationary::GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, + const int &aRowOffset, const int &aColumnOffset, + Locations &aLocation1, Locations &aLocation2, + Locations &aLocation3, T *aLocalTheta, + const int &aDistanceMetric) { + + const T sigma_square = aLocalTheta[0]; + const T nu = aLocalTheta[2]; + int i0 = aRowOffset; + int flag = 0; + int j0; + int i, j; + T dist; + + for (i = 0; i < aRowsNumber; i++) { + j0 = aColumnOffset; + for (j = 0; j < aColumnsNumber; j++) { + dist = DistanceCalculationHelpers::CalculateDistance(aLocation1, aLocation2, i0, j0, aDistanceMetric, + flag); + dist = pow(dist, nu); + *(apMatrixA + i + j * aRowsNumber) = (dist == 0.0) ? sigma_square : sigma_square * exp(-(dist / aLocalTheta[1])); + j0++; + } + i0++; + } +} From b1c0165690ced1757dbe895c114dddf1932b146d Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 8 Jan 2024 13:39:30 +0200 Subject: [PATCH 02/82] all fixes --- CHANGELOG.md | 33 + CMakeLists.txt | 154 ++-- ExaGeoStatCPPConfig.cmake.in | 142 ++++ USER_MANUAL.md | 4 +- clean_build.sh | 23 +- cmake/ImportBlas.cmake | 44 +- cmake/ImportBlasPP.cmake | 65 +- cmake/ImportCatch2.cmake | 42 +- cmake/ImportChameleon.cmake | 62 +- cmake/ImportCuSolver.cmake | 20 - cmake/ImportGFortran.cmake | 17 - cmake/ImportGSL.cmake | 48 +- cmake/ImportHCore.cmake | 26 + cmake/ImportHcore.cmake | 50 -- cmake/ImportHiCMA.cmake | 64 +- cmake/ImportHwloc.cmake | 56 +- cmake/ImportLapack.cmake | 42 +- cmake/ImportLapackPP.cmake | 60 -- cmake/ImportNLOPT.cmake | 56 +- cmake/ImportOpenMP.cmake | 27 - cmake/ImportStarPu.cmake | 92 +- cmake/ImportStarsH.cmake | 86 +- cmake/macros/BuildDependency.cmake | 64 +- cmake/macros/ImportDependency.cmake | 70 ++ cmake/toolchains/CudaToolchain.cmake | 1 - config.sh | 30 +- docs/config.in | 21 +- exageostatcppConfig.cmake.in | 67 -- examples/configurations/CMakeLists.txt | 6 +- .../SyntheticDataGeneration.cpp | 15 +- .../SyntheticLocationsGeneration.cpp | 80 -- examples/end-to-end/DataGeneration.cpp | 8 +- .../end-to-end/DataGenerationAndModeling.cpp | 10 +- .../DataGenerationModelingAndPrediction.cpp | 15 +- examples/end-to-end/DataModeling.cpp | 13 +- examples/end-to-end/DataPrediction.cpp | 12 +- inst/include/api/ExaGeoStat.hpp | 6 +- inst/include/common/Definitions.hpp | 13 + inst/include/common/PluginRegistry.hpp | 10 +- .../include/configurations/Configurations.hpp | 11 +- .../include/data-generators/DataGenerator.hpp | 5 +- .../concrete/CSVDataGenerator.hpp | 15 +- .../concrete/SyntheticGenerator.hpp | 4 +- inst/include/data-units/DescriptorData.hpp | 13 +- .../data-units/ModelingDataHolders.hpp | 4 +- .../descriptor/ExaGeoStatDescriptor.hpp | 3 +- .../concrete/ChameleonDescriptor.hpp | 3 +- .../descriptor/concrete/HicmaDescriptor.hpp | 3 +- inst/include/hardware/ExaGeoStatHardware.hpp | 4 +- .../helpers/DistanceCalculationHelpers.hpp | 8 + inst/include/kernels/Kernel.hpp | 8 +- .../concrete/UnivariateMaternNonStat.hpp | 141 ---- .../UnivariateMaternNonStationary.hpp | 72 -- .../LinearAlgebraMethods.hpp | 642 ++++++++++---- .../concrete/HicmaHeaders.hpp | 2 +- .../chameleon/ChameleonImplementation.hpp | 24 +- .../tile-low-rank/HicmaImplementation.hpp | 28 +- inst/include/prediction/Prediction.hpp | 8 +- inst/include/prediction/PredictionHelpers.hpp | 5 +- package.pc.in | 2 +- scripts/Benchmarking.sh | 67 ++ src/CMakeLists.txt | 7 +- src/api/ExaGeoStat.cpp | 24 +- src/configurations/Configurations.cpp | 183 ++-- .../concrete/CSVDataGenerator.cpp | 43 +- .../concrete/SyntheticGenerator.cpp | 49 +- src/data-units/DescriptorData.cpp | 28 +- .../descriptor/ExaGeoStatDescriptor.cpp | 20 +- .../descriptor/concrete/CMakeLists.txt | 2 +- .../concrete/ChameleonDescriptor.cpp | 4 +- .../descriptor/concrete/HicmaDescriptor.cpp | 4 +- src/hardware/ExaGeoStatHardware.cpp | 10 +- src/helpers/CommunicatorMPI.cpp | 4 + src/helpers/DistanceCalculationHelpers.cpp | 6 +- src/kernels/CMakeLists.txt | 24 +- src/kernels/Kernel.cpp | 11 +- .../concrete/BivariateMaternFlexible.cpp | 8 +- .../concrete/BivariateMaternParsimonious.cpp | 8 +- .../BivariateSpacetimeMaternStationary.cpp | 6 +- .../concrete/TrivariateMaternParsimonious.cpp | 8 +- .../concrete/UnivariateExpNonGaussian.cpp | 7 +- .../concrete/UnivariateMaternDbeta.cpp | 10 +- .../concrete/UnivariateMaternDdbetaBeta.cpp | 12 +- .../concrete/UnivariateMaternDdbetaNu.cpp | 12 +- .../concrete/UnivariateMaternDdnuNu.cpp | 12 +- .../UnivariateMaternDdsigmaSquare.cpp | 4 +- .../UnivariateMaternDdsigmaSquareBeta.cpp | 8 +- .../UnivariateMaternDdsigmaSquareNu.cpp | 6 +- src/kernels/concrete/UnivariateMaternDnu.cpp | 8 +- .../concrete/UnivariateMaternDsigmaSquare.cpp | 6 +- .../concrete/UnivariateMaternNonGaussian.cpp | 8 +- .../concrete/UnivariateMaternNonStat.cpp | 145 ---- .../UnivariateMaternNonStationary.cpp | 115 --- .../UnivariateMaternNuggetsStationary.cpp | 8 +- .../concrete/UnivariateMaternStationary.cpp | 2 +- .../UnivariateSpacetimeMaternStationary.cpp | 8 +- .../LinearAlgebraFactory.cpp | 6 +- .../LinearAlgebraMethods.cpp | 797 ++++++++++++------ .../concrete/CMakeLists.txt | 2 +- .../chameleon/ChameleonImplementation.cpp | 194 +++-- .../tile-low-rank/HicmaImplementation.cpp | 267 ++++-- src/prediction/Prediction.cpp | 98 ++- .../PredictionAuxiliaryFunctions.cpp | 7 +- src/prediction/PredictionHelpers.cpp | 62 +- src/results/Results.cpp | 70 +- tests/cpp-tests/api/TestExaGeoStatApi.cpp | 20 +- .../concrete/TestCSVDataGenerator.cpp | 124 +-- .../concrete/TestSyntheticGenerator.cpp | 24 +- tests/cpp-tests/kernels/CMakeLists.txt | 2 - .../concrete/TestBivariateMaternFlexible.cpp | 4 +- .../TestBivariateMaternParsimonious.cpp | 4 +- ...TestBivariateSpacetimeMaternStationary.cpp | 23 +- .../TestTrivariateMaternParsimonious.cpp | 4 +- .../TestUnivariateMaternNonGaussian.cpp | 12 +- .../concrete/TestUnivariateMaternNonStat.cpp | 75 -- .../TestUnivariateMaternNonStationary.cpp | 13 - .../TestUnivariateMaternNuggetsStationary.cpp | 4 +- .../TestUnivariateMaternStationary.cpp | 4 +- ...estUnivariateSpacetimeMaternStationary.cpp | 20 +- .../linear-algebra-solvers/CMakeLists.txt | 2 +- .../TestChameleonImplementationDST.cpp | 10 +- .../TestChameleonImplementationDense.cpp | 10 +- .../concrete/TestHiCMAImplementationTLR.cpp | 22 +- tests/heavy-tests/CMakeLists.txt | 14 + tests/heavy-tests/ExamplesTests.cpp | 97 +++ tests/heavy-tests/HeavyTests.cpp | 248 ++++++ tests/heavy-tests/README.md | 3 + 127 files changed, 3157 insertions(+), 2631 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 ExaGeoStatCPPConfig.cmake.in delete mode 100644 cmake/ImportCuSolver.cmake delete mode 100644 cmake/ImportGFortran.cmake create mode 100644 cmake/ImportHCore.cmake delete mode 100644 cmake/ImportHcore.cmake delete mode 100644 cmake/ImportLapackPP.cmake delete mode 100644 cmake/ImportOpenMP.cmake create mode 100644 cmake/macros/ImportDependency.cmake delete mode 100644 exageostatcppConfig.cmake.in delete mode 100644 examples/data-generators/SyntheticLocationsGeneration.cpp delete mode 100644 inst/include/kernels/concrete/UnivariateMaternNonStat.hpp delete mode 100644 inst/include/kernels/concrete/UnivariateMaternNonStationary.hpp create mode 100644 scripts/Benchmarking.sh delete mode 100644 src/kernels/concrete/UnivariateMaternNonStat.cpp delete mode 100644 src/kernels/concrete/UnivariateMaternNonStationary.cpp delete mode 100644 tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonStat.cpp delete mode 100644 tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonStationary.cpp create mode 100644 tests/heavy-tests/CMakeLists.txt create mode 100644 tests/heavy-tests/ExamplesTests.cpp create mode 100644 tests/heavy-tests/HeavyTests.cpp create mode 100644 tests/heavy-tests/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..48461b8f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] - Version 1.0.1 (YYYY-MM-DD) +### Added +- Implemented a new changelog. +- Introduced a benchmarking script. + +### Fixed +- Resolved issues with MPI installation. +- Fixed the printing of configuration summaries with MPI. +- Automated the process of adding a new kernel. +- Improved packaging of software with CPack. +- Addressed installation issues. +- Fixed bivariate and trivariate kernel functionality. +- Corrected time-space kernel issues. + +### Changed +- Updated the installation process for dependencies. +- Modified the calculation of P for all kernels. +- Adjusted CMake variables. +- Revised the process of finding BlasPP and Catch2 libraries. +- Updated doxygen documentation. + +### Removed +- Eliminated non-stationary kernel support. +- Removed Find OpenMP, LapackPP, and CuSolver. + +## [1.0.0] - 2023-11-12 +### Added +- Integrated all features present in [ExaGeoStat C version](https://github.com/ecrc/exageostat). diff --git a/CMakeLists.txt b/CMakeLists.txt index 6740d8a5..7a43ab00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ # The project is a parallel high performance unified framework for geographical statistics on manycore systems. # The file sets up variables and finds dependencies required for the project. # It also provides options to enable building tests, building examples, building documentation, and enabling a packaging system for distribution. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-01-30 @@ -20,10 +20,11 @@ cmake_policy(SET CMP0048 NEW) # Set project options option(USE_CUDA "Use Cuda, if available" false) option(USE_MPI "Use MPI, if available" false) -option(EXAGEOSTAT_BUILD_TESTS "Option to enable building tests" ON) -option(EXAGEOSTAT_BUILD_EXAMPLES "Option to enable building examples" ON) -option(EXAGEOSTAT_BUILD_DOCS "Build documentation in docs directory" ON) -option(EXAGEOSTAT_PACKAGE "Enable a packaging system for distribution" OFF) +option(BUILD_TESTS "Option to enable building tests" OFF) +option(BUILD_HEAVY_TESTS "Option to enable building heavy tests, This may take a lot of time" OFF) +option(BUILD_EXAMPLES "Option to enable building examples" ON) +option(BUILD_DOCS "Build documentation in docs directory" OFF) +option(CREATE_PACKAGE "Enable a packaging system for distribution" OFF) # Cmake Module Paths set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") @@ -44,21 +45,22 @@ else () endif () # Project Name and Version -project(exageostatcpp VERSION 1.0.0 DESCRIPTION "ExaGeoStat is a parallel high performance unified framework for geostatistics on manycore systems.") +project(ExaGeoStatCPP VERSION 1.0.0 DESCRIPTION "ExaGeoStatCPP is a parallel high performance unified framework for geostatistics on manycore systems.") # Show the current version of CMake. message(STATUS "CMAKE VERSION: ${CMAKE_VERSION}") # Enable C++ language enable_language(CXX) - +# Get the current path of the project. add_compile_definitions(PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}/") - +# Add Paths definitions add_definitions( -DLOG_PATH="${PROJECT_SOURCE_DIR}/synthetic_ds/" -DKERNELS_PATH="${PROJECT_SOURCE_DIR}/inst/include/kernels/concrete/" ) -# Add all dependencies for ExaGeoStat PP +# ExaGeoStatCPP depends on a CUDA +# ------------------------------- if (USE_CUDA) message("-- Build CUDA Support") else () @@ -67,73 +69,70 @@ else () unset(BLA_VENDOR) endif () -# EXAGEOSTAT depends on a MPI +# ExaGeoStatCPP depends on a MPI # ------------------------------- if (USE_MPI) # Enable MPI and include MPI add_definitions(-DUSE_MPI=TRUE) message(STATUS "Trying to find MPI") find_package(MPI REQUIRED) + include_directories(${MPI_INCLUDE_PATH}) + list(APPEND LIBS ${MPI_LIBRARIES}) list(APPEND STARPU_COMPONENT_LIST "MPI") endif () -# EXAGEOSTAT depends on LAPACKE +# ExaGeoStatCPP depends on LAPACKE #----------------------------- find_package(LAPACKE) list(APPEND LIBS ${LAPACKE_LIBRARIES}) link_directories(${LAPACKE_LIBRARY_DIRS_DEP}) include_directories(${LAPACKE_INCLUDE_DIRS}) -# Check if no path is set for installation -if (NOT EXAGEOSTAT_INSTALL_PREFIX) - message(FATAL_ERROR "Installation path not set! Please use -DEXAGEOSTAT_INSTALL_PREFIX=path/to/install or use ./config.sh") -endif () -# Print installation path of Exageostat. -message(STATUS "Installation path : ${EXAGEOSTAT_INSTALL_PREFIX}") +# Add all dependencies for ExaGeoStatCPP +#----------------------------- -# EXAGEOSTAT depends on a Hwloc +# Print installation path of ExaGeoStatCPP. +message(STATUS "Installation path : ${CMAKE_INSTALL_PREFIX}") + +# ExaGeoStatCPP depends on a HWLoc # ------------------------------- include(ImportHwloc) list(APPEND STARPU_COMPONENT_LIST "HWLOC") - string(REPLACE ";" " " STARPU_COMPONENT_STRING "${STARPU_COMPONENT_LIST}") -# EXAGEOSTAT depends on a runtime +# ExaGeoStatCPP depends on a StarPu runtime # ------------------------------- include(ImportStarPu) -# EXAGEOSTAT depends on a GSL +# ExaGeoStatCPP depends on a GSL # ------------------------------- include(ImportGSL) -# EXAGEOSTAT depends on a NLOPT +# ExaGeoStatCPP depends on a NLOPT # ------------------------------- include(ImportNLOPT) -# EXAGEOSTAT depends on HiCMA +# ExaGeoStatCPP depends on HiCMA # ------------------------------- -if (EXAGEOSTAT_USE_HICMA) - add_definitions(-DEXAGEOSTAT_USE_HICMA=TRUE) - message(STATUS "Add Hcore, Dependency needed for HiCMA") - include(ImportHcore) - message(STATUS "Add StarsH, Dependency needed for HiCMA") +if (USE_HICMA) + add_definitions(-DUSE_HICMA=TRUE) + include(ImportHCore) include(ImportStarsH) include(ImportHiCMA) endif () -# EXAGEOSTAT depends on CHAMELEON +# ExaGeoStatCPP depends on Chameleon # ------------------------------- include(ImportChameleon) -# EXAGEOSTAT depends on a LAPACK/BLASPP +# ExaGeoStatCPP depends on a LAPACK/BlasPP # ------------------------------- include(ImportBlasPP) include(ImportLapack) -# EXAGEOSTAT DOCUMENTATIONS -if (EXAGEOSTAT_BUILD_DOCS) +# ExaGeoStatCPP Documentation +if (BUILD_DOCS) find_package(Doxygen) - if (DOXYGEN_FOUND) add_subdirectory("docs") else () @@ -141,7 +140,7 @@ if (EXAGEOSTAT_BUILD_DOCS) endif () endif () -# Include directories for Exageostat-cpp +# Include directories for ExaGeoStatCPP include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inst/include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prerequisites) set(MY_LOGGER_PATH ${CMAKE_CURRENT_SOURCE_DIR}) @@ -158,14 +157,8 @@ target_link_libraries(${PROJECT_NAME}_INTERFACE INTERFACE ${PROJECT_NAME}) # Add linker options to the target target_link_options(${PROJECT_NAME}_INTERFACE INTERFACE "SHELL:-Wl,--whole-archive $ -Wl,--no-whole-archive") -# Install headers -install(TARGETS exageostatcpp - DESTINATION lib/ - PUBLIC_HEADER DESTINATION include/ - ) - # Add tests if enabled -if (${EXAGEOSTAT_BUILD_TESTS}) +if (${BUILD_TESTS}) message(STATUS "Building Tests") include(ImportCatch2) include(Catch) @@ -174,55 +167,85 @@ if (${EXAGEOSTAT_BUILD_TESTS}) enable_testing() endif () +# Add heavy tests if enabled +if (${BUILD_HEAVY_TESTS}) + message(STATUS "Building Heavy Tests") + include(ImportCatch2) + include(Catch) + include(CTest) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests/heavy-tests) + enable_testing() + message(STATUS "Building heavy tests will enable examples too") + set(BUILD_EXAMPLES ON) +endif () -if (EXAGEOSTAT_BUILD_EXAMPLES) - message(STATUS "Building Examples is Enabled") +# Add examples if enabled +if (BUILD_EXAMPLES) + message(STATUS "Building Examples is Enabled") add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples) endif () -# Installation actions -install(DIRECTORY include/${PROJECT_NAME} DESTINATION include) +# ExaGeoStatCPP Dependence export messages for users +# ------------------------------- +message(" \n \t ** Configurations of ExaGeoStatCPP and installation of dependence is done successfully ** ") +message("\t - Export the following line to avoid re-install dependencies each time. -") +message("\t ----------------------------------------------------------------------------------------------------------------------------------- ") +message("\t export PKG_CONFIG_PATH=${CMAKE_INSTALL_PREFIX}/CHAMELEON/lib/pkgconfig:$PKG_CONFIG_PATH") +message("\t export PKG_CONFIG_PATH=${CMAKE_INSTALL_PREFIX}/STARPU/lib/pkgconfig:$PKG_CONFIG_PATH") +message("\t export PKG_CONFIG_PATH=${CMAKE_INSTALL_PREFIX}/HWLOC/lib/pkgconfig:$PKG_CONFIG_PATH") +message("\t export PKG_CONFIG_PATH=${CMAKE_INSTALL_PREFIX}/GSL/lib/pkgconfig:$PKG_CONFIG_PATH") +message("\t export PKG_CONFIG_PATH=${CMAKE_INSTALL_PREFIX}/NLOPT/lib/pkgconfig:${CMAKE_INSTALL_PREFIX}/NLOPT/lib64/pkgconfig:$PKG_CONFIG_PATH") +if(USE_HICMA) + message("\t export PKG_CONFIG_PATH=${CMAKE_INSTALL_PREFIX}/STARSH/lib/pkgconfig:$PKG_CONFIG_PATH") + message("\t export PKG_CONFIG_PATH=${CMAKE_INSTALL_PREFIX}/HCORE/lib/pkgconfig:$PKG_CONFIG_PATH") + message("\t export PKG_CONFIG_PATH=${CMAKE_INSTALL_PREFIX}/HICMA/lib/pkgconfig:$PKG_CONFIG_PATH") +endif() +message("\t ----------------------------------------------------------------------------------------------------------------------------------- \n") + +# Installation of ExaGeoStatCPP +install(DIRECTORY inst/include/ DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/include) + ## Install cmake find package. include(CMakePackageConfigHelpers) -write_basic_package_version_file("${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" COMPATIBILITY ExactVersion) +write_basic_package_version_file("${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/${PROJECT_NAME}ConfigVersion.cmake" COMPATIBILITY ExactVersion) install( FILES - "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" - DESTINATION lib/cmake/${PROJECT_NAME} + "${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/cmake/${PROJECT_NAME} ) configure_file(${PROJECT_NAME}Config.cmake.in - ${PROJECT_NAME}Config.cmake @ONLY) + ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/${PROJECT_NAME}Config.cmake @ONLY) install( FILES - "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - DESTINATION lib/cmake/${PROJECT_NAME} + "${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/${PROJECT_NAME}Config.cmake" + DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/cmake/${PROJECT_NAME} ) install( DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cmake" - DESTINATION lib/cmake/${PROJECT_NAME}/Modules + DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/Modules ) ## Generate pkg-config file configure_file(package.pc.in - lib/pkgconfig/${PROJECT_NAME}.pc @ONLY) + ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/pkgconfig/${PROJECT_NAME}.pc @ONLY) install( FILES - "${PROJECT_BINARY_DIR}/lib/pkgconfig/${PROJECT_NAME}.pc" - DESTINATION lib/pkgconfig/ + "${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/pkgconfig/${PROJECT_NAME}.pc" + DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/pkgconfig/ ) -if (EXAGEOSTAT_PACKAGE) +if (CREATE_PACKAGE) ################## # Release source # ################## set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/README.md) - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ExaGeoStat is a parallel high performance unified framework for geostatistics on manycore systems. Its abbreviation stands for 'Exascale Geostatistics'.") + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ExaGeoStatCPP is a parallel high performance unified framework for geostatistics on manycore systems. Its abbreviation stands for 'Exascale Geostatistics'.") set(CPACK_PACKAGE_VERSION "${${PROJECT_NAME}_VERSION}") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") @@ -230,16 +253,11 @@ if (EXAGEOSTAT_PACKAGE) set(CPACK_PACKAGE_CONTACT "sameh.abdulah@kaust.edu.sa") set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_SOURCE_DIR}/README.md) set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE) - set(CPACK_SOURCE_IGNORE_FILES "bin;.git;.gitmodules;Jenkinsfile") + set(CPACK_SOURCE_IGNORE_FILES "bin;.git;Jenkinsfile") include(CPack) -endif () -message(" \n \t ** Configurations of ExaGeoStat and installation of dependence is done successfully ** ") -message("\t Export the following line to avoid re-install dependencies each time, This line assume that you haven't changed the installation path. ") -message("\t If not, Please change the following paths with your installation path. \n") -message("\t ------------------------------------------------------------------------------------------------------------------------------- ") -message("\t export PKG_CONFIG_PATH=${EXAGEOSTAT_INSTALL_PREFIX}/CHAMELEON/lib/pkgconfig:${EXAGEOSTAT_INSTALL_PREFIX}/GSL/lib/pkgconfig:$PKG_CONFIG_PATH") -message("\t export PKG_CONFIG_PATH=${EXAGEOSTAT_INSTALL_PREFIX}/HCORE/lib/pkgconfig:${EXAGEOSTAT_INSTALL_PREFIX}/HICMA/lib/pkgconfig:$PKG_CONFIG_PATH") -message("\t export PKG_CONFIG_PATH=${EXAGEOSTAT_INSTALL_PREFIX}/HWLOC/lib/pkgconfig:${EXAGEOSTAT_INSTALL_PREFIX}/NLOPT/lib64/pkgconfig:$PKG_CONFIG_PATH") -message("\t export PKG_CONFIG_PATH=${EXAGEOSTAT_INSTALL_PREFIX}/STARPU/lib/pkgconfig:${EXAGEOSTAT_INSTALL_PREFIX}/STARSH/lib/pkgconfig:$PKG_CONFIG_PATH") -message("\t ------------------------------------------------------------------------------------------------------------------------------- \n") + message("\t - Export the following line if you want to use ExaGeoStatCPP in another software. -") + message("\t ----------------------------------------------------------------------------------------------------------------------------------------------------- ") + message("\t export PKG_CONFIG_PATH=${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/pkgconfig:$PKG_CONFIG_PATH") + message("\t ----------------------------------------------------------------------------------------------------------------------------------------------------- ") +endif () \ No newline at end of file diff --git a/ExaGeoStatCPPConfig.cmake.in b/ExaGeoStatCPPConfig.cmake.in new file mode 100644 index 00000000..89c24af2 --- /dev/null +++ b/ExaGeoStatCPPConfig.cmake.in @@ -0,0 +1,142 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file ExaGeoStatCPPConfig.cmake.in +# @version 1.0.0 +# @author Mahmoud ElKarargy +# @date 2023-01-30 + + +# relocatable package +@PACKAGE_INIT@ + +# defined since 2.8.3 +if (CMAKE_VERSION VERSION_LESS 2.8.3) + get_filename_component(CMAKE_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) +endif () + +include("${CMAKE_CURRENT_LIST_DIR}/lib/cmake/ExaGeoStatCPPCoreConfig.cmake") +# Compute the installation prefix relative to this file. +get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) +if (_IMPORT_PREFIX STREQUAL "/") + set(_IMPORT_PREFIX "") +endif () + + +# Cmake Module Paths +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH};${CMAKE_CURRENT_LIST_DIR}/Modules/cmake) + +set(ENV{PKG_CONFIG_PATH} "${_IMPORT_PREFIX}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}") +include_directories(${_IMPORT_PREFIX}/include) +link_directories(${_IMPORT_PREFIX}/lib) +set(BLA_PREFER_PKGCONFIG "ON") + +set(EXAGEOSTATCPP_LIBRARIES ExaGeoStatCPP) +set(EXAGEOSTATCPP_LIBRARY_DIRS "${_IMPORT_PREFIX}/lib") +set(EXAGEOSTATCPP_INCLUDE_DIRS "${_IMPORT_PREFIX}/include") + +set(USE_CUDA "@USE_CUDA@") + +# Select toolchain based on whether CUDA is enabled or not +if (USE_CUDA) + # Enable CUDA and include CudaToolchain + add_definitions(-DUSE_CUDA=TRUE) + enable_language(CUDA) + include(toolchains/CudaToolchain) + # Set BLA_VENDOR to NVHPC for CUDA-enabled builds + set(BLA_VENDOR NVHPC) + list(APPEND STARPU_COMPONENT_LIST "CUDA") +else () + message("-- Build x86 Support") + # Include GccToolchain for non-CUDA builds - Gcc + include(toolchains/GccToolchain) +endif () + +add_definitions( + -DLOG_PATH="${PROJECT_SOURCE_DIR}/synthetic_ds/" + -DKERNELS_PATH="${PROJECT_SOURCE_DIR}/inst/include/kernels/concrete/" +) + + +# EXAGEOSTAT depends on a MPI +# ------------------------------- +if (USE_MPI) + # Enable MPI and include MPI + add_definitions(-DUSE_MPI=TRUE) + message(STATUS "Trying to find MPI") + find_package(MPI REQUIRED) + include_directories(${MPI_INCLUDE_PATH}) + list(APPEND LIBS ${MPI_LIBRARIES}) + list(APPEND STARPU_COMPONENT_LIST "MPI") +endif () + +# EXAGEOSTAT depends on LAPACKE +#----------------------------- +find_package(LAPACKE) +list(APPEND LIBS ${LAPACKE_LIBRARIES}) +link_directories(${LAPACKE_LIBRARY_DIRS_DEP}) +include_directories(${LAPACKE_INCLUDE_DIRS}) + + +# EXAGEOSTAT depends on a Hwloc +# ------------------------------- +include(ImportHwloc) +list(APPEND STARPU_COMPONENT_LIST "HWLOC") + +string(REPLACE ";" " " STARPU_COMPONENT_STRING "${STARPU_COMPONENT_LIST}") + +# EXAGEOSTAT depends on a runtime +# ------------------------------- +include(ImportStarPu) + +# EXAGEOSTAT depends on a GSL +# ------------------------------- +include(ImportGSL) + +# EXAGEOSTAT depends on a NLOPT +# ------------------------------- +include(ImportNLOPT) + + +# EXAGEOSTAT depends on HiCMA +# ------------------------------- +if (USE_HICMA) + add_definitions(-DUSE_HICMA=TRUE) + message(STATUS "Add Hcore, Dependency needed for HiCMA") + include(ImportHCore) + message(STATUS "Add StarsH, Dependency needed for HiCMA") + include(ImportStarsH) + include(ImportHiCMA) +endif () + +# EXAGEOSTAT depends on CHAMELEON +# ------------------------------- +include(ImportChameleon) + +# EXAGEOSTAT depends on a LAPACK/BLASPP +# ------------------------------- +include(ImportBlasPP) +include(ImportLapack) + +# Add all dependencies for ExaGeoStatCPP +if (USE_CUDA) + message("-- Build CUDA Support") +else () + message("-- Build x86 Support") + set(gpu_backend CACHE STRING "none" FORCE) + unset(BLA_VENDOR) +endif () + +find_package_handle_standard_args(ExaGeoStatCPP + NAME_MISMATCHED + REQUIRED_VARS EXAGEOSTATCPP_INCLUDE_DIRS EXAGEOSTATCPP_LIBRARY_DIRS EXAGEOSTATCPP_LIBRARIES + VERSION_VAR EXAGEOSTATCPP_VERSION + ) + +# Cleanup temporary variables. +set(_IMPORT_PREFIX) +if (CMAKE_VERSION VERSION_LESS 2.8.3) + set(CMAKE_CURRENT_LIST_DIR) +endif () diff --git a/USER_MANUAL.md b/USER_MANUAL.md index e8a35059..1ca31c58 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -239,7 +239,7 @@ can be used to train the software to predict the values of new data better. After you provide your arguments with the Configurations module, you must do the following two steps: ```c++ // Create a new ExaGeoStat data that holds the locations and descriptors data. -ExaGeoStatData data; +std::unique_ptr> data; // Generate data by passing your arguments through the configurations, hardware, and container of the data, which will be filled with the newly generated data. ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); @@ -253,7 +253,7 @@ ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); - Then do the following two steps. ```c++ // Create a new ExaGeoStat data that holds the locations data and descriptors data. -ExaGeoStatData data; +std::unique_ptr> data; // Generate data by passing your arguments through the configurations, your hardware and your container of the data which will be filled with the new generated data. ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); ``` diff --git a/clean_build.sh b/clean_build.sh index 8d56b8a9..2802742f 100755 --- a/clean_build.sh +++ b/clean_build.sh @@ -12,9 +12,10 @@ # Define variables. verbose="" num_proc="-j $(nproc)" # Use the number of available processors by default. +installation=0 # Parse command-line arguments. -while getopts "vj:h" opt; do +while getopts "vj:hi" opt; do case $opt in v) verbose="VERBOSE=1" @@ -24,6 +25,9 @@ while getopts "vj:h" opt; do num_proc="-j $OPTARG" echo "Using $OPTARG threads to build" ;; + i) + installation=1 + ;; h) # Print help information and exit. echo "Usage: $(basename "$0") [-v] [-j ] [-h]" @@ -31,8 +35,9 @@ while getopts "vj:h" opt; do echo "" echo "Options:" echo " -v Use verbose output." - echo " -j Build with a specific number of threads." + echo " -i To install the software" echo " -h Show this help message." + echo " -j Build with a specific number of threads." exit 0 ;; *) @@ -50,5 +55,15 @@ cd bin/ || { } # Clean the directory and build the code with the specified options. -make clean -make all $num_proc $verbose \ No newline at end of file +cmake --build . $num_proc $verbose + +# Install the software if the -i option is provided. +if [ "$installation" -eq 1 ]; then + cmake --install . +fi + +# Check the value of EXAGEOSTAT_PACKAGE variable in CMakeCache.txt +if grep -q "EXAGEOSTAT_PACKAGE:BOOL=ON" CMakeCache.txt; then + echo "CPack is enabled. Packaging the project." + cpack +fi \ No newline at end of file diff --git a/cmake/ImportBlas.cmake b/cmake/ImportBlas.cmake index 2df0a6c9..2a488917 100644 --- a/cmake/ImportBlas.cmake +++ b/cmake/ImportBlas.cmake @@ -4,38 +4,20 @@ # @file ImportBlas.cmake # @brief This file searches for the BLAS library and includes it if not already included. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy -# @author Sameh Abdulah # @date 2023-03-12 -# search for BLAS library, if not already included -message("") -message("---------------------------------------- BLAS") -message(STATUS "Checking for BLAS") -include(macros/BuildDependency) +#Configurations +set(name "BLAS") +set(tag "v0.3.21") +set(version "0.3.21") +set(flag "") +set(is_cmake ON) +set(is_git ON) +set(auto_gen OFF) +set(url "https://github.com/xianyi/OpenBLAS") -if (NOT TARGET BLAS) - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(BLAS QUIET) - - if (BLAS_FOUND) - message(" Found BLAS: ${BLAS_LIBRARIES}") - else () - message(" Can't find Blas, Installing it instead ..") - # Set installation flags - set(FLAGS "") - set(ISCMAKE ON) - set(ISGIT ON) - set(AUTO_GEN OFF) - set(build_tests "false") - BuildDependency(BLAS "https://github.com/xianyi/OpenBLAS" "v0.3.21" ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) - find_package(BLAS REQUIRED) - endif () - -else () - message(" BLAS already included") -endif () - -message(STATUS "BLAS done") +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +message(STATUS "${name} done") diff --git a/cmake/ImportBlasPP.cmake b/cmake/ImportBlasPP.cmake index 32f25b0b..53a099a6 100644 --- a/cmake/ImportBlasPP.cmake +++ b/cmake/ImportBlasPP.cmake @@ -4,51 +4,30 @@ # @file ImportBlasPP.cmake # @brief This file searches for the BLAS++ library and includes it if not already included. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy -# @author Sameh Abdulah # @date 2023-03-12 -# search for BLAS library, if not already included -message("") -message("---------------------------------------- BLAS++") -message(STATUS "Checking for BLAS++") - -if (NOT TARGET blaspp) - - include(FindPkgConfig) - find_package(PkgConfig QUIET) - include(ImportBlas) - - find_package(blaspp QUIET) - - if (blaspp_FOUND) - message("Found BLAS++: ${blaspp_DIR}") - elseif (EXISTS "${CMAKE_SOURCE_DIR}/blaspp/CMakeLists.txt") - set(build_tests_save "${build_tests}") - set(build_tests "false") - add_subdirectory("blaspp") - - set(build_tests "${build_tests_save}") - set(blaspp_DIR "${CMAKE_BINARY_DIR}/blaspp") - else () - set(build_tests_save "${build_tests}") - set(build_tests "false") - set(url "https://github.com/icl-utk-edu/blaspp") - set(tag "v2023.01.00") - message(STATUS "Fetching BLAS++ ${tag} from ${url}") - include(FetchContent) - FetchContent_Declare( - blaspp GIT_REPOSITORY "${url}" GIT_TAG "${tag}") - FetchContent_MakeAvailable(blaspp) - set(build_tests "${build_tests_save}") - endif () -else () - message(" BLAS++ already included") +include(ImportBlas) + +#Configurations +set(name blaspp) +string(TOUPPER ${name} capital_name) +set(tag "v2023.01.00") +# Set installation flags +if (USE_CUDA) + set(flag "-Dgpu_backend=cuda") +else() + set(flag "-Dgpu_backend=") endif () -set(LIBS - blaspp - ${LIBS} - ) -message(STATUS "BLAS++ done") +set(version "2023.01.00") +set(is_cmake ON) +set(is_git ON) +set(auto_gen OFF) +set(url "https://github.com/icl-utk-edu/blaspp") + +set(${name}_DIR "${CMAKE_INSTALL_PREFIX}/${capital_name}/lib/cmake/${name}") +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +message(STATUS "${name} done") diff --git a/cmake/ImportCatch2.cmake b/cmake/ImportCatch2.cmake index 5e70e789..54531022 100644 --- a/cmake/ImportCatch2.cmake +++ b/cmake/ImportCatch2.cmake @@ -5,36 +5,20 @@ # @file ImportChameleon.cmake # @brief This script checks for Chameleon and includes it in the project if it is not already a target. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy -# @author Sameh Abdulah # @date 2023-03-13 -message("") -message("---------------------------------------- Catch2") -message(STATUS "Checking for Catch2") -include(macros/BuildDependency) +#Configurations +set(name "Catch2") +set(tag "v3.3.2") +set(version "3.3.2") +set(flag "") +set(is_cmake ON) +set(is_git ON) +set(auto_gen OFF) +set(url "https://github.com/catchorg/Catch2.git") -IF (NOT TARGET Catch2) - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(Catch2 QUIET) - - # If Catch2 is not found, fetch and build it - if (Catch2_FOUND) - message(" Found Catch2") - else () - message(" Can't find catch2, Installing it instead ..") - include(FetchContent) - set(FETCHCONTENT_QUIET OFF) - FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.3.2 # Replace with the version of Catch2 you want to use for v3 - GIT_SHALLOW TRUE - ) - FetchContent_MakeAvailable(Catch2) - endif () -else () - message(STATUS "Catch2 already included") -endif () +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +message(STATUS "${name} done") diff --git a/cmake/ImportChameleon.cmake b/cmake/ImportChameleon.cmake index 4d19a5ea..c724fe4a 100644 --- a/cmake/ImportChameleon.cmake +++ b/cmake/ImportChameleon.cmake @@ -5,61 +5,25 @@ # @file ImportChameleon.cmake # @brief This script checks for Chameleon and includes it in the project if it is not already a target. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 -message("") -message("---------------------------------------- Chameleon") -message(STATUS "Checking for Chameleon") -include(macros/BuildDependency) +#Configurations +set(name "CHAMELEON") +set(tag "v1.1.0") +set(version "1.1.0") +set(flag -DCHAMELEON_USE_CUDA=${USE_CUDA} \-DBLA_VENDOR=${BLA_VENDOR} \-DCHAMELEON_USE_MPI=${USE_MPI} \-DCHAMELEON_SCHED_STARPU=ON \-DCHAMELEON_ENABLE_TESTING=OFF) +set(is_cmake ON) +set(is_git ON) +set(auto_gen OFF) +set(url "https://gitlab.inria.fr/solverstack/chameleon.git") -if (NOT TARGET CHAMELEON_FOUND) - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(CHAMELEON QUIET) +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) - # If Chameleon is found, include it - if (CHAMELEON_FOUND) - message(" Found Chameleon: ${CHAMELEON_LIBDIR}") - # If Chameleon is not found, install it - else () - message(" Can't find Chameleon, Installing it instead ..") - # Set installation flags - set(FLAGS -DCHAMELEON_USE_CUDA=${USE_CUDA} \-DBLA_VENDOR=${BLA_VENDOR} \-DCHAMELEON_USE_MPI=${USE_MPI} \-DCHAMELEON_SCHED_STARPU=ON \-DCHAMELEON_ENABLE_TESTING=OFF) - set(ISCMAKE ON) - set(ISGIT ON) - set(AUTO_GEN OFF) - # Install Chameleon - BuildDependency(CHAMELEON "https://gitlab.inria.fr/solverstack/chameleon.git" "v1.1.0" ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) - # Reset flags - set(FLAGS "") - find_package(CHAMELEON REQUIRED) - endif () -else() - message(" CHAMELEON already included") -endif() - -link_directories(${CHAMELEON_LIBRARY_DIRS_DEP}) - -include_directories(AFTER ${CHAMELEON_INCLUDE_DIRS_DEP}) include_directories(AFTER ${CHAMELEON_DIR_FOUND}/include/coreblas) include_directories(${CHAMELEON_DIR_FOUND}/chameleon-src) -if (CHAMELEON_LINKER_FLAGS) - list(APPEND CMAKE_EXE_LINKER_FLAGS "${CHAMELEON_LINKER_FLAGS} ") -endif () -if (CHAMELEON_LIBRARY_DIRS) - # the RPATH to be used when installing - list(APPEND CMAKE_INSTALL_RPATH "${CHAMELEON_LIBRARY_DIRS}") -endif () -if (CHAMELEON_LIBRARIES) - if (CHAMELEON_LIBRARIES_DEP) - list(APPEND LIBS ${CHAMELEON_LIBRARIES_DEP}) - else () - list(APPEND LIBS ${CHAMELEON_LIBRARIES}) - endif () -endif () - -message(STATUS "Chameleon done") +message(STATUS "${name} done") diff --git a/cmake/ImportCuSolver.cmake b/cmake/ImportCuSolver.cmake deleted file mode 100644 index bdaa0978..00000000 --- a/cmake/ImportCuSolver.cmake +++ /dev/null @@ -1,20 +0,0 @@ - -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# @file ImportCuSolver.cmake -# @brief This script sets CUDA libraries and adds CuSolver, CuBlas, and CuBlasLt to the list of libraries. -# @version 1.0.0 -# @author Mahmoud ElKarargy -# @author Sameh Abdulah -# @date 2023-03-13 - -set(cudart_lib CUDA::cudart) -set(cublas_lib CUDA::cublas) -set(LIBS - CUDA::cusolver - CUDA::cublas - CUDA::cublasLt - ${LIBS} - ) diff --git a/cmake/ImportGFortran.cmake b/cmake/ImportGFortran.cmake deleted file mode 100644 index a57f0517..00000000 --- a/cmake/ImportGFortran.cmake +++ /dev/null @@ -1,17 +0,0 @@ - -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# @file ImportGFortran.cmake -# @brief Defines the gfortran library for use in the project. -# @version 1.0.0 -# @author Mahmoud ElKarargy -# @author Sameh Abdulah -# @date 2023-03-14 - -# Export the list of libraries, including gfortran, for use in the project. -set(LIBS - gfortran - ${LIBS} - ) diff --git a/cmake/ImportGSL.cmake b/cmake/ImportGSL.cmake index 3c111a1b..b3aac862 100644 --- a/cmake/ImportGSL.cmake +++ b/cmake/ImportGSL.cmake @@ -5,47 +5,25 @@ # @file ImportGSL.cmake # @brief Checks for the GSL library and includes it in the project if it is not already present. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-16 -message("") -message("---------------------------------------- GSL") -message(STATUS "Checking for GSL") +#Configurations +set(name "GSL") +set(tag "v2.7.1") +set(version "2.7.1") +set(flag "") +set(is_cmake OFF) +set(is_git OFF) +set(auto_gen OFF) +set(url "https://ftp.gnu.org/gnu/gsl/gsl-2.7.1.tar.gz") -include(macros/BuildDependency) - -# Check if the GSL library is already included in the project. -if (NOT TARGET GSL_FOUND) - - # If not, attempt to find it with PkgConfig and CMake's find_package function. - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(GSL QUIET) - - # If the GSL library is found, add it to the project's libraries. - if (GSL_FOUND) - message(" Found GSL: ${GSL_INCLUDE_DIRS}") - else () - - # If the GSL library is not found, install it and add it to the project's libraries. - message(" Can't find GSL, Installing it instead ..") - set(FLAGS "") - set(ISCMAKE OFF) - set(ISGIT OFF) - set(AUTO_GEN OFF) - BuildDependency(GSL "https://ftp.gnu.org/gnu/gsl/gsl-2.7.1.tar.gz" "v2.7.1" ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) - find_package(GSL REQUIRED) - endif () -else () - message(" GSL already included") -endif () +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) # Add the GSL library to the project's list of libraries. list(APPEND LIBS gsl) -include_directories(${GSL_INCLUDE_DIRS}) -link_directories(${GSL_LIBRARY_DIRS}) -message("${GSL_LIBRARIES}") -message(STATUS "GSL done") +message(STATUS "${name} done") diff --git a/cmake/ImportHCore.cmake b/cmake/ImportHCore.cmake new file mode 100644 index 00000000..5ec2a558 --- /dev/null +++ b/cmake/ImportHCore.cmake @@ -0,0 +1,26 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file ImportHCore.cmake +# @brief Checks for the Hcore library and includes it in the project if it is not already present. +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @author Sameh Abdulah +# @date 2023-03-15 + +#Configurations +set(name "HCORE") +set(tag "v0.1.3") +set(version "0.1.3") +set(flag "") +set(is_cmake ON) +set(is_git ON) +set(auto_gen OFF) +set(url "https://github.com/ecrc/hcore.git") + +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) + +message(STATUS "${name} done") diff --git a/cmake/ImportHcore.cmake b/cmake/ImportHcore.cmake deleted file mode 100644 index 0c5fe43f..00000000 --- a/cmake/ImportHcore.cmake +++ /dev/null @@ -1,50 +0,0 @@ - -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# @file ImportHcore.cmake -# @brief Checks for the Hcore library and includes it in the project if it is not already present. -# @version 1.0.0 -# @author Mahmoud ElKarargy -# @author Sameh Abdulah -# @date 2023-03-15 - -message("") -message("---------------------------------------- Hcore") -message(STATUS "Checking for Hcore") - -include(macros/BuildDependency) - -# Check if the Hcore library is already included in the project. -if (NOT TARGET HCORE_FOUND) - - # If not, attempt to find it with PkgConfig and CMake's find_package function. - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(HCORE QUIET) - - # If the Hcore library is found, add it to the project's libraries. - if (HCORE_FOUND) - message(" Found Hcore: ${HCORE_LIBDIR}") - else() - - # If the Hcore library is not found, install it and add it to the project's libraries. - message(" Can't find Hcore, Installing it instead ..") - set(FLAGS "") - set(ISCMAKE ON) - set(ISGIT ON) - set(AUTO_GEN OFF) - BuildDependency(HCORE "https://github.com/ecrc/hcore.git" "v0.1.3" ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) - find_package(HCORE REQUIRED) - endif() -else() - message(" HCORE already included") -endif() - -# Add the Hcore library to the project's list of libraries. -list(APPEND LIBS ${HCORE_LIBRARIES}) -link_directories(${HCORE_LIBRARY_DIRS_DEP}) -include_directories(${HCORE_INCLUDE_DIRS}) - -message(STATUS "Hcore done") \ No newline at end of file diff --git a/cmake/ImportHiCMA.cmake b/cmake/ImportHiCMA.cmake index 025ce48f..9efadc8e 100644 --- a/cmake/ImportHiCMA.cmake +++ b/cmake/ImportHiCMA.cmake @@ -5,62 +5,24 @@ # @file ImportHiCMA.cmake # @brief Find and include HiCMA library as a dependency. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 -message("") -message("---------------------------------------- Hicma") -message(STATUS "Checking for HiCMA") -include(macros/BuildDependency) +#Configurations +set(name "HICMA") +set(tag "v1.0.0") +set(version "1.0.0") +set(flag -DHICMA_USE_MPI=${USE_MPI}) +set(is_cmake ON) +set(is_git ON) +set(auto_gen OFF) +set(url "https://github.com/ecrc/hicma.git") -if (NOT TARGET HICMA_FOUND) - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(HICMA QUIET) - - # If HiCMA is found, print its location. - if (HICMA_FOUND) - message(" Found HiCMA: ${HICMA_LIBDIR}") - # If not found, install it. - else () - message(" Can't find HiCMA, Installing it instead ..") - - # Set the flags to be passed to the build command. - set(FLAGS \-DHICMA_USE_MPI=${USE_MPI}) - set(ISCMAKE ON) - set(ISGIT ON) - set(AUTO_GEN OFF) - # Build HiCMA from source. - BuildDependency(HiCMA "https://github.com/ecrc/hicma.git" "v1.0.0" ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) - # Clear the flags. - set(FLAGS "") - # Find HiCMA after installation. - find_package(HICMA REQUIRED) - endif () -else () - message(" HiCMA already included") -endif () +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) # Include HiCMA headers in the project. -include_directories(${HICMA_INCLUDE_DIRS_DEP}) include_directories(${HICMA_LIBDIR}/../hicma-src/hicma_ext) - -# Include HiCMA libraries in the project. -if (HICMA_LINKER_FLAGS) - list(APPEND CMAKE_EXE_LINKER_FLAGS "${HICMA_LINKER_FLAGS}") -endif () -if (HICMA_LIBRARY_DIRS) - list(APPEND CMAKE_INSTALL_RPATH "${HICMA_LIBRARY_DIRS}") - link_directories(${HICMA_LIBRARY_DIRS}) -endif () - -# Add HiCMA libraries to the dependencies of the project. -if (HICMA_LIBRARIES_DEP) - list(APPEND LIBS ${HICMA_LIBRARIES_DEP}) -else () - list(APPEND LIBS ${HICMA_LIBRARIES}) -endif () - -message(STATUS "HiCMA done") \ No newline at end of file +message(STATUS "HiCMA done") diff --git a/cmake/ImportHwloc.cmake b/cmake/ImportHwloc.cmake index 019f8c82..1804dd4c 100644 --- a/cmake/ImportHwloc.cmake +++ b/cmake/ImportHwloc.cmake @@ -5,52 +5,22 @@ # @file ImportHwloc.cmake # @brief Find and include Hwloc library as a dependency. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-15 +#Configurations +set(name "HWLOC") +set(tag "hwloc-2.4.0") +set(version "2.4.0") +set(flag "") +set(is_cmake OFF) +set(is_git ON) +set(auto_gen ON) +set(url "https://github.com/open-mpi/hwloc") -message("") -message("---------------------------------------- Hwloc") -message(STATUS "Checking for Hwloc") +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) -include(macros/BuildDependency) - -# If Hwloc library is not already included as a target, try to find it. -if (NOT TARGET HWLOC) - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(HWLOC 2.4.0 REQUIRED QUIET) - - # If Hwloc is found, print its location. - if (HWLOC_FOUND) - message(" Found HWLOC: ${HWLOC_INCLUDE_DIRS}") - # If not found, install it. - else () - message(" Can't find Hwloc, Installing it instead ..") - - # Set the flags to be passed to the build command. - set(FLAGS "") - set(ISCMAKE OFF) - set(ISGIT ON) - set(AUTO_GEN ON) - - # Build Hwloc from source. - BuildDependency(HWLOC "https://github.com/open-mpi/hwloc" "hwloc-2.4.0" ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) - - # Find Hwloc after installation. - find_package(HWLOC 2.4.0 REQUIRED) - endif () -else () - message(" HWLOC already included") -endif () - -# Include Hwloc libraries in the project. -list(APPEND LIBS ${HWLOC_LIBRARIES}) -link_directories(${HWLOC_LIBRARY_DIRS_DEP}) - -# Include Hwloc headers in the project. -include_directories(${HWLOC_INCLUDE_DIRS}) - -message(STATUS "HWLOC done") +message(STATUS "${name} done") diff --git a/cmake/ImportLapack.cmake b/cmake/ImportLapack.cmake index c0e77417..64e7a6a0 100644 --- a/cmake/ImportLapack.cmake +++ b/cmake/ImportLapack.cmake @@ -5,38 +5,22 @@ # @file ImportLapack.cmake # @brief Find and include LAPACK library as a dependency. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-12 -# search for LAPACK library, if not already included -message("") -message("---------------------------------------- LAPACK") -message(STATUS "Checking for LAPACK") +#Configurations +set(name "LAPACK") +set(tag "v0.3.21") +set(version "0.3.21") +set(flag "") +set(is_cmake ON) +set(is_git ON) +set(auto_gen OFF) +set(url "https://github.com/xianyi/OpenBLAS") -include(macros/BuildDependency) +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) -if (NOT TARGET LAPACK) - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(LAPACK QUIET) - - if (LAPACK_FOUND) - message(" Found LAPACK: ${LAPACK_LIBRARIES}") - else () - message(" Can't find Blas, Installing it instead ..") - # Set installation flags - set(FLAGS "") - set(ISCMAKE ON) - set(ISGIT ON) - set(AUTO_GEN OFF) - set(build_tests "false") - BuildDependency(LAPACK "https://github.com/xianyi/OpenBLAS" "v0.3.21" ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) - find_package(LAPACK REQUIRED) - endif () -else () - message(" LAPACK already included") -endif () - -message(STATUS "LAPACK done") +message(STATUS "${name} done") diff --git a/cmake/ImportLapackPP.cmake b/cmake/ImportLapackPP.cmake deleted file mode 100644 index 543c3cd7..00000000 --- a/cmake/ImportLapackPP.cmake +++ /dev/null @@ -1,60 +0,0 @@ - -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# @file ImportLapackPP.cmake -# @brief Find and include LAPACK++ library as a dependency. -# @version 1.0.0 -# @author Mahmoud ElKarargy -# @author Sameh Abdulah -# @date 2023-03-12 - -# search for LAPACK library, if not already included -message("") -message("---------------------------------------- LAPACK++") -message(STATUS "Checking for LAPACK++") -if (NOT TARGET lapackpp) - include(ImportLapack) - - find_package(lapackpp QUIET) - if (lapackpp_FOUND) - message(" Found LAPACK++: ${lapackpp_DIR}") - elseif (EXISTS "${CMAKE_SOURCE_DIR}/lapackpp/CMakeLists.txt") - set(build_tests_save "${build_tests}") - set(build_tests "false") - - add_subdirectory("lapackpp") - - set(build_tests "${build_tests_save}") - set(lapackpp_DIR "${CMAKE_BINARY_DIR}/lapackpp") - else () - set(build_tests_save "${build_tests}") - set(build_tests "false") - - set(url "https://github.com/icl-utk-edu/lapackpp") - set(tag "v2023.01.00") - message(STATUS "Fetching LAPACK++ ${tag} from ${url}") - include(FetchContent) - FetchContent_Declare( - lapackpp GIT_REPOSITORY "${url}" GIT_TAG "${tag}") - FetchContent_MakeAvailable(lapackpp) - - set(build_tests "${build_tests_save}") - endif () -else () - message(" LAPACK++ already included") -endif () - -# Add to linking libs. -set(LIBS - lapackpp - ${LIBS} - ) - -# Add definition indicating version. -if ("${lapackpp_defines}" MATCHES "LAPACK_ILP64") - set(COMPILE_DEFINITIONS "${COMPILE_DEFINITIONS} -DHCORE_HAVE_LAPACK_WITH_ILP64") -endif () - -message(STATUS "LAPACK++ done") diff --git a/cmake/ImportNLOPT.cmake b/cmake/ImportNLOPT.cmake index 3a510b8b..8c8feade 100644 --- a/cmake/ImportNLOPT.cmake +++ b/cmake/ImportNLOPT.cmake @@ -5,52 +5,22 @@ # @file ImportNLOPT.cmake # @brief Find and include NLOPT library as a dependency. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-26 -message("") -message("---------------------------------------- NLOPT") -message(STATUS "Checking for NLOPT") -include(macros/BuildDependency) -if (NOT TARGET NLOPT_FOUND) - # Try to find NLOPT. - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(NLOPT 2.4.2 QUIET) +#Configurations +set(name "NLOPT") +set(tag "v2.7.1") +set(version "2.7.1") +set(flag " ") +set(is_cmake ON) +set(is_git ON) +set(auto_gen OFF) +set(url "https://github.com/stevengj/nlopt") - # If NLOPT is found, print its location. - if (NLOPT_FOUND) - message(" Found NLOPT: ${NLOPT_LIBRARIES}") - # If not found, install it. - else () - message(" Can't find NLOPT, Installing it instead ..") +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) - # Set installation flags. - set(FLAGS "") - set(ISCMAKE ON) - set(ISGIT ON) - set(AUTO_GEN OFF) - # Build NLOPT from source. - BuildDependency(NLOPT "https://github.com/stevengj/nlopt" "v2.7.1" ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) - - # Set the location of NLOPT. - set(NLOPT_LIBRARY_DIRS= ${EXAGEOSTAT_INSTALL_PREFIX}/NLOPT/lib:$NLOPT_LIBRARY_DIRS) - set(NLOPT_INCLUDE_DIRS= ${EXAGEOSTAT_INSTALL_PREFIX}/NLOPT/include:$NLOPT_INCLUDE_DIRS) - - # Try to find NLOPT again. - find_package(NLOPT 2.4.2 REQUIRED) - endif () -else () - message(" NLOPT already included") -endif () - -# Include NLOPT headers. -include_directories(${NLOPT_INCLUDE_DIRS}) - -# Link NLOPT libraries. -link_directories(${NLOPT_LIBRARY_DIRS}) -list(APPEND LIBS ${NLOPT_LIBRARIES}) - -message(STATUS "NLOPT done") \ No newline at end of file +message(STATUS "${name} done") diff --git a/cmake/ImportOpenMP.cmake b/cmake/ImportOpenMP.cmake deleted file mode 100644 index 4ec5aec9..00000000 --- a/cmake/ImportOpenMP.cmake +++ /dev/null @@ -1,27 +0,0 @@ - -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# @file ImportOpenMP.cmake -# @brief Find and include OpenMP library as a dependency. -# @version 1.0.0 -# @author Mahmoud ElKarargy -# @author Sameh Abdulah -# @date 2023-03-13 - -# Add OpenMP if requested. -option(USE_OPENMP "Use OpenMP, if available" true) -if (NOT USE_OPENMP) - message(STATUS "User has requested to NOT use OpenMP") -else () - find_package(OpenMP QUIET) - IF (OPENMP_FOUND) - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(LIBS - OpenMP::OpenMP_CXX - ${LIBS} - ) - ENDIF () -endif () diff --git a/cmake/ImportStarPu.cmake b/cmake/ImportStarPu.cmake index 42e6c47e..84509c9a 100644 --- a/cmake/ImportStarPu.cmake +++ b/cmake/ImportStarPu.cmake @@ -5,74 +5,32 @@ # @file ImportSTARPU.cmake # @brief Find and include STARPU library as a dependency. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 -message("") -message("---------------------------------------- StarPU") -message(STATUS "Checking for StarPU") - -include(macros/BuildDependency) - -if (NOT TARGET STARPU) - # Try to find STARPU. - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(STARPU 1.3.9 QUIET COMPONENTS ${STARPU_COMPONENT_LIST}) - - - # If STARPU is found, print its location. - if (STARPU_FOUND) - message(" Found StarPU: ${STARPU_LIBRARIES}") - # If not found, install it. - else () - # Set the flags to be passed to the build command. - set(ISCMAKE OFF) - set(ISGIT ON) - set(AUTO_GEN ON) - - if (USE_CUDA AND USE_MPI) - message(STATUS "Downloading STARPU - MPI CUDA" ) - set(FLAGS \--enable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--enable-mpi) - elseif(USE_CUDA) - message(STATUS "Downloading STARPU - CUDA" ) - set(FLAGS \--enable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--disable-mpi) - elseif(USE_MPI) - message(STATUS "Downloading STARPU - MPI" ) - set(FLAGS \--disable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--enable-mpi) - else() - message(STATUS "Downloading STARPU - SERIAL" ) - set(FLAGS \--disable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--disable-mpi) - endif() - - BuildDependency(STARPU "https://gitlab.inria.fr/starpu/starpu.git" "starpu-1.3.9" ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) - # Clear the flags. - set(FLAGS "") - # Find StarPU after installation. - find_package(STARPU 1.3.9 QUIET COMPONENTS ${STARPU_COMPONENT_LIST}) - - endif () -else () - message(" STARPU already included") -endif () - -# Include STARPU headers. -list(APPEND LIBS ${STARPU_LIBRARIES}) -link_directories(${STARPU_LIBRARY_DIRS_DEP}) -include_directories(${STARPU_INCLUDE_DIRS}) -include_directories(${STARPU_INCLUDE_DIRS}/runtime/starpu) -include_directories(${STARPU_INCLUDE_DIRS_DEP}) - -# Set linker flags. -if (STARPU_LINKER_FLAGS) - list(APPEND CMAKE_EXE_LINKER_FLAGS "${STARPU_LINKER_FLAGS}") -endif () -set(CMAKE_REQUIRED_INCLUDES "${STARPU_INCLUDE_DIRS_DEP}") -foreach (libdir ${STARPU_LIBRARY_DIRS_DEP}) - list(APPEND CMAKE_REQUIRED_FLAGS "-L${libdir}") -endforeach () -set(CMAKE_REQUIRED_LIBRARIES "${STARPU_LIBRARIES_DEP}") - -message(STATUS "starpu done") +#Configurations +set(name "STARPU") +set(tag "starpu-1.3.9") +set(version "1.3.9") + +if (USE_CUDA AND USE_MPI) + set(flag \--enable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--enable-mpi) +elseif(USE_CUDA) + set(flag \--enable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--disable-mpi) +elseif(USE_MPI) + set(flag \--disable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--enable-mpi) +else() + set(flag \--disable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--disable-mpi) +endif() + +set(is_cmake OFF) +set(is_git ON) +set(auto_gen ON) +set(url "https://gitlab.inria.fr/starpu/starpu.git") + +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) + +message(STATUS "${name} done") diff --git a/cmake/ImportStarsH.cmake b/cmake/ImportStarsH.cmake index 585fecbb..b74b4409 100644 --- a/cmake/ImportStarsH.cmake +++ b/cmake/ImportStarsH.cmake @@ -5,82 +5,22 @@ # @file CMakeLists.txt # @brief Find and include STARSH library as a dependency. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 -message("") -message("---------------------------------------- Stars-H") -message(STATUS "Checking for STARSH") -include(macros/BuildDependency) +#Configurations +set(name "STARSH") +set(tag "v0.3.1") +set(version "0.3.1") +set(flag \-DSTARPU=OFF \-DMPI=${USE_MPI}) +set(is_cmake ON) +set(is_git ON) +set(auto_gen OFF) +set(url "https://github.com/ecrc/stars-h.git") -if (NOT TARGET STARSH_FOUND) - # Try to find STARSH. - include(FindPkgConfig) - find_package(PkgConfig QUIET) - find_package(STARSH QUIET) +include(macros/ImportDependency) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) - # If STARSH is found, print its location. - if (STARSH_FOUND) - message(" Found STARSH: ${STARSH_INCLUDE_DIRS}") - # If not found, install it. - else () - message(" Can't find STARSH, Installing it instead ..") - set(FLAGS \-DSTARPU=OFF \-DMPI=${USE_MPI}) - set(ISCMAKE ON) - set(ISGIT ON) - set(AUTO_GEN OFF) - BuildDependency(STARSH "https://github.com/ecrc/stars-h.git" "v0.3.1" ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) - set(FLAGS "") - find_package(STARSH REQUIRED) - endif () -else () - message(" STARSH already included") -endif () - -# Include STARSH headers. -include_directories(${STARSH_INCLUDE_DIRS_DEP}) - -# Set linker flags and library directories. -if (STARSH_LINKER_FLAGS) - list(APPEND CMAKE_EXE_LINKER_FLAGS "${STARSH_LINKER_FLAGS}") -endif () -if (STARSH_LIBRARY_DIRS) - list(APPEND CMAKE_INSTALL_RPATH "${STARSH_LIBRARY_DIRS}") -endif () - -# Check if GSL is a dependency of STARSH and add it if needed. -if (STARSH_LIBRARIES) - find_library(_STARSH_LIB NAME starsh PATHS ${STARSH_LIBRARY_DIRS}) - if (_STARSH_LIB AND NOT "${STARSH_LIBRARIES_DEP}" MATCHES "gsl") - execute_process(COMMAND nm ${_STARSH_LIB} COMMAND grep gsl RESULT_VARIABLE GSL_IN_STARSH) - if (${GSL_IN_STARSH} EQUAL 0) - message(STATUS "STARSH depends on gsl. Adding it to dependency list") - find_package(GSL REQUIRED) - if (GSL_FOUND) - if (STARSH_LIBRARIES_DEP) - list(APPEND STARSH_LIBRARIES_DEP ${GSL_LIBRARIES}) - else () - list(APPEND STARSH_LIBRARIES ${GSL_LIBRARIES}) - endif () - endif () - endif () - endif () - - # Add STARSH libraries to the project. - if (STARSH_LIBRARIES_DEP) - list(APPEND LIBS ${STARSH_LIBRARIES_DEP}) - link_directories(${STARSH_LIBRARY_DIRS_DEP}) - link_directories(${STARSH_LIBRARIES_DEP}) - else () - list(APPEND LIBS ${STARSH_LIBRARIES}) - link_directories(${STARSH_LIBRARIES}) - endif () - - list(APPEND LIBS ${STARSH_LIBRARIES}) - link_directories(${STARSH_LIBRARY_DIRS_DEP}) - include_directories(${STARSH_INCLUDE_DIRS}) -endif () - -message(STATUS "StarsH Done") \ No newline at end of file +message(STATUS "${name} done") diff --git a/cmake/macros/BuildDependency.cmake b/cmake/macros/BuildDependency.cmake index 5edee634..825db78b 100644 --- a/cmake/macros/BuildDependency.cmake +++ b/cmake/macros/BuildDependency.cmake @@ -4,25 +4,31 @@ # @file BuildDependency.cmake # @brief Fetches, builds, and installs a dependency. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy -# @author Sameh Abdulah # @date 2023-03-12 -# @param raw_name The name of the dependency. -# @param url The URL from which to fetch the dependency. -# @param tag The version or tag of the dependency to fetch. -# @param ${FLAGS} Additional flags to pass to the configure/make commands. -# @param ${ISCMAKE} A boolean flag indicating whether the dependency uses CMake as its build system. -# @param ${ISGIT} A boolean flag indicating whether the dependency is hosted on a git repository. -# @param ${AUTO_GEN} A boolean flag indicating whether to use autogen scripts or not. +# After building and installing the dependency, the macro installs the lib, include, and share directories +# in the current directory. -# This macro fetches the dependency using CMake's FetchContent module, and then builds and installs it. -# It also sets several environment variables (LD_LIBRARY_PATH, LIBRARY_PATH, CPATH, PKG_CONFIG_PATH, -# and ${capital_name}_DIR) and includes and links to the installation directory of the dependency. +# BuildDependency Macro: +# This macro is designed to fetch, configure, build, and install a dependency. +# It takes the following parameters: +# - raw_name: The name of the dependency. +# - url: The URL of the repository or source tarball. +# - tag: The version or tag of the dependency to fetch. +# - flags: Additional flags to pass to the configure/make commands. +# - is_using_cmake: A boolean flag indicating whether the dependency uses CMake as its build system. +# - is_using_git: A boolean flag indicating whether the dependency is hosted on a git repository. +# - auto_generation: A boolean flag indicating whether to use autogen scripts or not. + +# The macro fetches the dependency using CMake's FetchContent module, depending on whether it's a git repo or not. +# It sets up build paths and creates a directory for build artifacts. The subproject is then configured using +# CMake or autotools, and finally, it's built and installed. Environment variables are set, and the dependency's +# lib, include, and share directories are installed in the current directory. + +macro(BuildDependency raw_name url tag flags is_using_cmake is_using_git auto_generation) -# After building and installing the dependency, the macro installs the lib, include, and share directories in the current directory. -macro(BuildDependency raw_name url tag ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) # Set the name of the dependency. string(TOLOWER ${raw_name} name) string(TOUPPER ${raw_name} capital_name) @@ -30,8 +36,9 @@ macro(BuildDependency raw_name url tag ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) # Fetch the dependency, depending on whether it's a git repo or not. message(STATUS "Fetching ${name} ${tag} from ${url}") include(FetchContent) - set(FETCHCONTENT_BASE_DIR ${EXAGEOSTAT_INSTALL_PREFIX}/${capital_name}/) - if (ISGIT) + set(FETCHCONTENT_BASE_DIR ${CMAKE_INSTALL_PREFIX}/${capital_name}) + + if (${is_using_git}) FetchContent_Declare(${name} GIT_REPOSITORY "${url}" GIT_TAG "${tag}" @@ -41,27 +48,27 @@ macro(BuildDependency raw_name url tag ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) else () FetchContent_Declare(${name} URL "${url}") endif () + FetchContent_Populate(${name}) - # Set up build paths and create directory for build artifacts. - set(${name}_srcpath ${EXAGEOSTAT_INSTALL_PREFIX}/${capital_name}/${name}-src) + # Set up build paths and create a directory for build artifacts. + set(${name}_srcpath ${CMAKE_INSTALL_PREFIX}/${capital_name}/${name}-src) set(${name}_binpath ${${name}_srcpath}/bin) - set(${name}_installpath ${EXAGEOSTAT_INSTALL_PREFIX}/${capital_name}/) + set(${name}_installpath ${CMAKE_INSTALL_PREFIX}/${capital_name}) file(MAKE_DIRECTORY ${${name}_binpath}) # Configure subproject. - if (ISCMAKE) - execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${EXAGEOSTAT_INSTALL_PREFIX}/${capital_name}/ ${FLAGS} + if (${is_using_cmake}) + execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/${capital_name} ${flags} ${${name}_srcpath} - WORKING_DIRECTORY - ${${name}_binpath}) + WORKING_DIRECTORY ${${name}_binpath}) else () - if (AUTO_GEN) + if (${auto_generation}) execute_process(COMMAND ./autogen.sh WORKING_DIRECTORY ${${name}_srcpath} COMMAND_ERROR_IS_FATAL ANY) endif () - execute_process(COMMAND ./configure --prefix=${EXAGEOSTAT_INSTALL_PREFIX}/${capital_name}/ ${FLAGS} + execute_process(COMMAND ./configure --prefix=${CMAKE_INSTALL_PREFIX}/${capital_name} ${flags} WORKING_DIRECTORY ${${name}_srcpath} COMMAND_ERROR_IS_FATAL ANY) endif () @@ -69,7 +76,7 @@ macro(BuildDependency raw_name url tag ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) # Build and install subproject. include(ProcessorCount) ProcessorCount(N) - if (ISCMAKE) + if (${is_using_cmake}) execute_process(COMMAND make -j ${N} WORKING_DIRECTORY ${${name}_binpath} COMMAND_ERROR_IS_FATAL ANY) @@ -90,7 +97,8 @@ macro(BuildDependency raw_name url tag ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) set(ENV{LIBRARY_PATH} "${${name}_installpath}/lib:${${name}_installpath}/lib64:$ENV{LIBRARY_PATH}") set(ENV{CPATH} "${${name}_installpath}/include:$ENV{CPATH}") set(ENV{PKG_CONFIG_PATH} "${${name}_installpath}/lib/pkgconfig:${${name}_installpath}/lib64/pkgconfig:$ENV{PKG_CONFIG_PATH}") - set(${capital_name}_DIR "${${name}_installpath}") + + # Include and link to the installation directory of the dependency include_directories(${${name}_installpath}/include) link_directories(${${name}_installpath}/lib) @@ -113,4 +121,4 @@ macro(BuildDependency raw_name url tag ${FLAGS} ${ISCMAKE} ${ISGIT} ${AUTO_GEN}) DESTINATION . ) -endmacro() +endmacro() \ No newline at end of file diff --git a/cmake/macros/ImportDependency.cmake b/cmake/macros/ImportDependency.cmake new file mode 100644 index 00000000..a7b2363e --- /dev/null +++ b/cmake/macros/ImportDependency.cmake @@ -0,0 +1,70 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file ImportDependency.cmake +# @brief CMake script for importing and building external dependencies. +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2023-12-28 + +# ImportDependency Macro: +# This macro is designed to check for the presence of an external dependency and install it if not found. +# It takes the following parameters: +# - raw_name: The name of the dependency. +# - tag: The tag of the dependency to fetch. +# - version: The version of the dependency. +# - url: The URL of the repository or source tarball. +# - flag: Additional flags to pass to the configure/make commands. +# - is_cmake: A boolean flag indicating whether the dependency uses CMake as its build system. +# - is_git: A boolean flag indicating whether the dependency is hosted on a git repository. +# - auto_gen: A boolean flag indicating whether to use autogen scripts or not. + +# The macro checks whether the dependency is already included. If not, it attempts to find the package. +# If the package is found, it prints a message. If not, it calls the BuildDependency macro to fetch, +# configure, build, and install the dependency. Finally, it attempts to find the package again to validate the installation. + +macro(ImportDependency name tag version url flag is_cmake is_git auto_gen) + + # First, Check if no path is set for installation. + if (CMAKE_INSTALL_PREFIX MATCHES "/usr/") + message(WARNING "Installation path not specified. Please set the installation path using -DCMAKE_INSTALL_PREFIX=path/to/install or execute ./config.sh. Otherwise, please note that administrative privileges may be required to install in system paths.") + endif () + + # Convert name to uppercase for consistency + string(TOUPPER ${name} capital_name) + message("") + message("---------------------------------------- ${capital_name}") + message(STATUS "Checking for ${capital_name} with Version ${version}") + + # Include the BuildDependency macro + include(macros/BuildDependency) + + # Check if the target is already included + IF (NOT TARGET ${name}) + include(FindPkgConfig) + find_package(PkgConfig QUIET) + find_package(${name} ${version} QUIET) + + # If the package is found, print a message + if (${name}_FOUND) + message(" Found ${capital_name}; ${${name}_DIR} ${${name}_LIBRARIES}") + else () + # If the package is not found, install it using BuildDependency + message(" Can't find ${capital_name}, Installing it instead ..") + BuildDependency(${name} ${url} ${tag} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) + find_package(${name} ${version} REQUIRED) + endif () + else () + message(STATUS "${capital_name} already included") + endif () + + # Include and link to the installation directory of the dependency + link_directories(${${name}_LIBRARY_DIRS_DEP}) + link_directories(${${name}_LIBRARY_DIRS}) + include_directories(${${name}_INCLUDE_DIRS}) + include_directories(AFTER ${${name}_INCLUDE_DIRS_DEP}) + list(APPEND LIBS ${${name}_LIBRARIES}) + list(APPEND LIBS ${${name}_LIBRARIES_DEP}) + +endmacro() \ No newline at end of file diff --git a/cmake/toolchains/CudaToolchain.cmake b/cmake/toolchains/CudaToolchain.cmake index f9edaa67..8486c427 100644 --- a/cmake/toolchains/CudaToolchain.cmake +++ b/cmake/toolchains/CudaToolchain.cmake @@ -19,5 +19,4 @@ set(CUDA_ARCHITECTURES "35;50;72") # Find the CUDA toolkit find_package(CUDAToolkit REQUIRED) -# TODO: Cuda linking set(ENV{LDFLAGS} "-L$ENV{CUDA_DIR}/lib64") diff --git a/config.sh b/config.sh index 1f7d390b..91028941 100755 --- a/config.sh +++ b/config.sh @@ -18,15 +18,17 @@ NC='\033[0m' INSTALL_PREFIX=$PWD/installdir/_deps PROJECT_SOURCE_DIR=$(dirname "$0") BUILDING_TESTS="OFF" +BUILDING_HEAVY_TESTS="OFF" BUILDING_EXAMPLES="OFF" USING_HiCMA="OFF" -VERBOSE=OFF +VERBOSE="OFF" USE_CUDA="OFF" USE_MPI="OFF" BLAS_VENDOR="" +PACKAGE="OFF" # Parse command line options -while getopts ":tevhHi:cms" opt; do +while getopts ":tevhHi:cmspT" opt; do case $opt in i) ##### Define installation path ##### echo -e "${YELLOW}Installation path set to $OPTARG.${NC}" @@ -36,6 +38,10 @@ while getopts ":tevhHi:cms" opt; do echo -e "${GREEN}Building tests enabled.${NC}" BUILDING_TESTS="ON" ;; + T) ##### Building heavy tests enabled ##### + echo -e "${GREEN}Building heavy tests enabled.${NC}" + BUILDING_HEAVY_TESTS="ON" + ;; e) ##### Building examples enabled ##### echo -e "${GREEN}Building examples enabled.${NC}" BUILDING_EXAMPLES="ON" @@ -53,13 +59,17 @@ while getopts ":tevhHi:cms" opt; do USE_MPI=ON ;; v) ##### printing full output of make ##### - echo -e "${YELLOW}printing make with details.${NC}" + echo -e "${GREEN}printing make with details.${NC}" VERBOSE=ON ;; s) ##### Passing Blas vendor with mkl ##### - echo -e "${YELLOW}MKL as a Blas vendor${NC}" + echo -e "${GREEN}MKL as a Blas vendor${NC}" BLAS_VENDOR="Intel10_64lp" ;; + p) ##### Enabling packaging system for distribution ##### + echo -e "${GREEN}CPACK enabled${NC}" + PACKAGE=ON + ;; \?) ##### Error unknown option ##### echo "Option $OPTARG parameter is unknown, please -h for help" exit 1 @@ -73,6 +83,7 @@ while getopts ":tevhHi:cms" opt; do echo "" printf "%20s %s\n" "-i [path] :" "specify installation path, default = ${PWD}/installdir/_deps/" printf "%20s %s\n" "-t :" "to enable building tests." + printf "%20s %s\n" "-T :" "to enable building heavy tests." printf "%20s %s\n" "-e :" "to enable building examples." printf "%20s %s\n" "-H :" "to enable using HiCMA." printf "%20s %s\n" "-c :" "to enable using CUDA." @@ -80,6 +91,7 @@ while getopts ":tevhHi:cms" opt; do printf "%20s %s\n" "-v :" "to enable verbose printings." printf "%20s %s\n" "-d :" "to enable debug mode." printf "%20s %s\n" "-s :" "to manually pass MKL as your blas vendor." + printf "%20s %s\n" "-p :" "to enable a packaging system for distribution." printf "%20s %s\n" "-h :" "Help." echo "" exit 1 @@ -120,14 +132,16 @@ rm -rf bin/ mkdir -p bin/installdir cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DEXAGEOSTAT_INSTALL_PREFIX="${INSTALL_PREFIX}" \ - -DEXAGEOSTAT_BUILD_TESTS="${BUILDING_TESTS}" \ - -DEXAGEOSTAT_BUILD_EXAMPLES="${BUILDING_EXAMPLES}" \ - -DEXAGEOSTAT_USE_HICMA="${USING_HiCMA}" \ + -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ + -DBUILD_TESTS="${BUILDING_TESTS}" \ + -DBUILD_HEAVY_TESTS="${BUILDING_HEAVY_TESTS}" \ + -DBUILD_EXAMPLES="${BUILDING_EXAMPLES}" \ + -DUSE_HICMA="${USING_HiCMA}" \ -DCMAKE_VERBOSE_MAKEFILE:BOOL=${VERBOSE} \ -DUSE_CUDA="${USE_CUDA}" \ -DUSE_MPI="${USE_MPI}" \ -DBLA_VENDOR="${BLAS_VENDOR}" \ + -DCREATE_PACKAGE="${PACKAGE}" \ -H"${PROJECT_SOURCE_DIR}" \ -B"${PROJECT_SOURCE_DIR}/bin" \ -G "Unix Makefiles" diff --git a/docs/config.in b/docs/config.in index 0cafddd5..70c8be38 100644 --- a/docs/config.in +++ b/docs/config.in @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "ExaGeoStat CPP" +PROJECT_NAME = "ExaGeoStatCPP" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -44,7 +44,7 @@ PROJECT_NUMBER = # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "ExaGeoStat is a parallel high performance unified framework for geostatistics on manycore systems." +PROJECT_BRIEF = "ExaGeoStatCPP is a parallel high performance unified framework for geostatistics on manycore systems." # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -68,7 +68,7 @@ OUTPUT_DIRECTORY = "bin" # performance problems for the file system. # The default value is: NO. -CREATE_SUBDIRS = NO +CREATE_SUBDIRS = YES # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII @@ -162,7 +162,7 @@ FULL_PATH_NAMES = NO # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = "@CMAKE_SOURCE_DIR@" +STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -367,7 +367,7 @@ DISTRIBUTE_GROUP_DOC = NO # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. -GROUP_NESTED_COMPOUNDS = YES +GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that @@ -524,7 +524,7 @@ INTERNAL_DOCS = NO # and Mac users are advised to set this option to NO. # The default value is: system dependent. -CASE_SENSE_NAMES = NO +CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the @@ -790,10 +790,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../src/ \ ../inst/ - - - +INPUT = "../inst/include" + ../README.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -946,7 +943,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = "@CMAKE_CURRENT_SOURCE_DIR@/index.md" +USE_MDFILE_AS_MAINPAGE = ../README.md #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -959,7 +956,7 @@ USE_MDFILE_AS_MAINPAGE = "@CMAKE_CURRENT_SOURCE_DIR@/index.md" # also VERBATIM_HEADERS is set to NO. # The default value is: NO. -SOURCE_BROWSER = YES +SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. diff --git a/exageostatcppConfig.cmake.in b/exageostatcppConfig.cmake.in deleted file mode 100644 index b36a3e51..00000000 --- a/exageostatcppConfig.cmake.in +++ /dev/null @@ -1,67 +0,0 @@ - -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# defined since 2.8.3 -if (CMAKE_VERSION VERSION_LESS 2.8.3) - get_filename_component(CMAKE_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) -endif () - -# Compute the installation prefix relative to this file. -get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) -get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) -get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) -get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) -if (_IMPORT_PREFIX STREQUAL "/") - set(_IMPORT_PREFIX "") -endif () - -set(USE_CUDA "@USE_CUDA@") -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules/cmake) - -set(ENV{PKG_CONFIG_PATH} "${_IMPORT_PREFIX}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}") -include_directories(${_IMPORT_PREFIX}/include) -link_directories(${_IMPORT_PREFIX}/lib) -set(BLA_PREFER_PKGCONFIG "ON") - -if (USE_CUDA) - message("-- Exageostat-cpp built CUDA Support") - find_package(CUDAToolkit REQUIRED) - find_package(BLAS REQUIRED) - find_package(blaspp REQUIRED) - unset(BLA_VENDOR) - find_package(LAPACK REQUIRED) -else () - message("-- Exageostat-cpp built x86 Support") - set(gpu_backend CACHE "none" FORCE) - find_package(blaspp REQUIRED) - find_package(lapackpp REQUIRED) -endif () - -# Add component-configs. -include("${CMAKE_CURRENT_LIST_DIR}/exageostat-cppConfig.cmake") - -# Compute the installation prefix relative to this file. -get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) -get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) -get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) -get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) -if (_IMPORT_PREFIX STREQUAL "/") - set(_IMPORT_PREFIX "") -endif () -set(exageostatcpp_LIBRARIES exageostatcpp) -set(exageostatcpp_LIBRARY_DIRS "${_IMPORT_PREFIX}/lib") -set(exageostatcpp_INCLUDE_DIRS "${_IMPORT_PREFIX}/include") - -find_package_handle_standard_args(exageostatcpp - NAME_MISMATCHED - REQUIRED_VARS exageostatcpp_INCLUDE_DIRS exageostatcpp_LIBRARY_DIRS exageostatcpp_LIBRARIES - VERSION_VAR exageostatcpp_VERSION - ) - -# Cleanup temporary variables. -set(_IMPORT_PREFIX) -if (CMAKE_VERSION VERSION_LESS 2.8.3) - set(CMAKE_CURRENT_LIST_DIR) -endif () diff --git a/examples/configurations/CMakeLists.txt b/examples/configurations/CMakeLists.txt index 769ba295..cfd13983 100644 --- a/examples/configurations/CMakeLists.txt +++ b/examples/configurations/CMakeLists.txt @@ -9,8 +9,8 @@ # @author Mahmoud ElKarargy # @date 2023-01-31 -# Define an executable named "Example_Synthetic_Data_Configurations". -add_executable(Example_Synthetic_Data_Configurations ${CMAKE_CURRENT_SOURCE_DIR}/ConfigurationModule.cpp) +# Define an executable named "Example_Configurations". +add_executable(Example_Configurations ${CMAKE_CURRENT_SOURCE_DIR}/ConfigurationModule.cpp) # Link the executable with the ExaGeoStat library and other libraries. -target_link_libraries(Example_Synthetic_Data_Configurations ${PROJECT_NAME}_INTERFACE) \ No newline at end of file +target_link_libraries(Example_Configurations ${PROJECT_NAME}_INTERFACE) \ No newline at end of file diff --git a/examples/data-generators/SyntheticDataGeneration.cpp b/examples/data-generators/SyntheticDataGeneration.cpp index 1c4323df..9490524f 100644 --- a/examples/data-generators/SyntheticDataGeneration.cpp +++ b/examples/data-generators/SyntheticDataGeneration.cpp @@ -43,14 +43,15 @@ int main(int argc, char **argv) { synthetic_data_configurations.GetCoresNumber(), synthetic_data_configurations.GetGPUsNumbers()); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(synthetic_data_configurations.GetKernelName()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + synthetic_data_configurations.GetKernelName(), synthetic_data_configurations.GetTimeSlot()); // Create a unique pointer to a DataGenerator object unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( synthetic_data_configurations); // Initialize the locations of the generated data - auto data = *synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); + auto data = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); // Define a struct to hold pointers to the x, y, and z coordinates of the generated data struct DataPointers { double *x; @@ -59,9 +60,9 @@ int main(int argc, char **argv) { } data_pointers{}; // Set the pointers in the DataPointers struct to the location coordinates of the generated data - data_pointers.x = data.GetLocations()->GetLocationX(); - data_pointers.y = data.GetLocations()->GetLocationY(); - data_pointers.z = data.GetLocations()->GetLocationZ(); + data_pointers.x = data->GetLocations()->GetLocationX(); + data_pointers.y = data->GetLocations()->GetLocationY(); + data_pointers.z = data->GetLocations()->GetLocationZ(); // Print the generated location coordinates LOGGER("Generated Data ...") @@ -76,7 +77,9 @@ int main(int argc, char **argv) { if (synthetic_data_configurations.GetDimension() != Dimension2D) { LOGGER_PRECISION(" Z: " << data_pointers.z[i], 18) } - LOGGER_PRECISION(" Measurements: " << ((double *)data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc->mat)[i] << "\n" , 18) + LOGGER_PRECISION(" Measurements: " << ((double *) data->GetDescriptorData()->GetDescriptor( + exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc->mat)[i] + << "\n", 18) } delete pKernel; diff --git a/examples/data-generators/SyntheticLocationsGeneration.cpp b/examples/data-generators/SyntheticLocationsGeneration.cpp deleted file mode 100644 index cb9b7880..00000000 --- a/examples/data-generators/SyntheticLocationsGeneration.cpp +++ /dev/null @@ -1,80 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file SyntheticLocationsGeneration.cpp - * @brief This file contains the main function for generating synthetic Locations for ExaGeoStat - * @version 1.0.0 - * @author Mahmoud ElKarargy - * @date 2023-03-04 -**/ - -#include - -#include -#include - -using namespace std; - -using namespace exageostat::configurations; -using namespace exageostat::generators; -using namespace exageostat::common; -using namespace exageostat::hardware; - -/** - * @brief The main function of the program. - * @details This function generates synthetic data for ExaGeoStat using the provided command line arguments. - * @param[in] argc The number of command line arguments. - * @param[in] argv The command line arguments. - * @return The status code of the program. - */ - -int main(int argc, char **argv) { - - // Create a new synthetic_data_configurations object with the provided command line arguments - Configurations synthetic_data_configurations; - synthetic_data_configurations.InitializeArguments(argc, argv); - - // initialize ExaGeoStat Hardware. - auto hardware = ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), - synthetic_data_configurations.GetCoresNumber(), - synthetic_data_configurations.GetGPUsNumbers()); - - // Create a unique pointer to a DataGenerator object - unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( - synthetic_data_configurations); - - // Initialize the locations of the generated data - auto *locations = synthetic_generator->CreateLocationsData(synthetic_data_configurations); - - // Define a struct to hold pointers to the x, y, and z coordinates of the generated data - struct DataPointers { - double *x; - double *y; - double *z; - } data_pointers{}; - - // Set the pointers in the DataPointers struct to the location coordinates of the generated data - data_pointers.x = locations->GetLocationX(); - data_pointers.y = locations->GetLocationY(); - data_pointers.z = locations->GetLocationZ(); - - // Print the generated location coordinates - LOGGER("Generated Locations are .. ") - int timeSlot; - if (synthetic_data_configurations.GetDimension() != DimensionST) { - timeSlot = 1; - } else { - timeSlot = synthetic_data_configurations.GetTimeSlot(); - } - for (auto i = 0; i < synthetic_data_configurations.GetProblemSize() * timeSlot; i++) { - LOGGER_PRECISION("X: " << data_pointers.x[i] << " Y: " << data_pointers.y[i], 18) - if (synthetic_data_configurations.GetDimension() != Dimension2D) { - LOGGER_PRECISION(" Z: " << data_pointers.z[i], 18) - } - LOGGER("\n") - } - return 0; -} \ No newline at end of file diff --git a/examples/end-to-end/DataGeneration.cpp b/examples/end-to-end/DataGeneration.cpp index 5d3ad6b0..c497d9c1 100644 --- a/examples/end-to-end/DataGeneration.cpp +++ b/examples/end-to-end/DataGeneration.cpp @@ -33,14 +33,12 @@ int main(int argc, char **argv) { Configurations configurations; // Initialize the arguments with the provided command line arguments configurations.InitializeArguments(argc, argv); - LOGGER("** initialize ExaGeoStat hardware ** ") + // Initialize the ExaGeoStat Hardware auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); - LOGGER("** Create ExaGeoStat data **") - ExaGeoStatData data; - LOGGER("** Generate ExaGeoStat data ** ") + // Load data by either read from file or create synthetic data. + std::unique_ptr> data; ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); - LOGGER("** All example stages have been completed successfully ** ") return 0; } \ No newline at end of file diff --git a/examples/end-to-end/DataGenerationAndModeling.cpp b/examples/end-to-end/DataGenerationAndModeling.cpp index 0b70d8a3..88c91aee 100644 --- a/examples/end-to-end/DataGenerationAndModeling.cpp +++ b/examples/end-to-end/DataGenerationAndModeling.cpp @@ -33,16 +33,14 @@ int main(int argc, char **argv) { Configurations configurations; // Initialize the arguments with the provided command line arguments configurations.InitializeArguments(argc, argv); - LOGGER("** initialize ExaGeoStat hardware ** ") + // Initialize the ExaGeoStat Hardware auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); - LOGGER("** Create ExaGeoStat data **") - ExaGeoStatData data; - LOGGER("** ExaGeoStat data generation ** ") + // Load data by either read from file or create synthetic data. + std::unique_ptr> data; ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); - LOGGER("** ExaGeoStat data Modeling ** ") + // Modeling module. ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); - LOGGER("** All example stages have been completed successfully ** ") return 0; } diff --git a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp index a1ec90ea..b9ade349 100644 --- a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp +++ b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp @@ -31,19 +31,18 @@ int main(int argc, char **argv) { // Create a new configurations object. Configurations configurations; - // Initialize the arguments with the provided command line arguments + // Initialize the arguments with the provided command line arguments configurations.InitializeArguments(argc, argv); - LOGGER("** Initialise ExaGeoStat hardware **") + // Initialize the ExaGeoStat Hardware auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); - LOGGER("** Create ExaGeoStat data **") - ExaGeoStatData data; - LOGGER("** ExaGeoStat data generation **") + // Load data by either read from file or create synthetic data. + std::unique_ptr> data; ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); - LOGGER("** ExaGeoStat data Modeling **") + // Modeling module. ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); - LOGGER("** ExaGeoStat data Prediction **") + // Prediction module ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data); - LOGGER("** All example stages have been completed successfully ** ") + return 0; } diff --git a/examples/end-to-end/DataModeling.cpp b/examples/end-to-end/DataModeling.cpp index 76706ea8..89832c55 100644 --- a/examples/end-to-end/DataModeling.cpp +++ b/examples/end-to-end/DataModeling.cpp @@ -47,13 +47,11 @@ int main(int argc, char **argv) { configurations.SetDenseTileSize(dts); // initialize ExaGeoStat hardware with the selected number of cores and gpus. - LOGGER("** initialize ExaGeoStat hardware ** ") auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); //Data Setup - LOGGER("** Create ExaGeoStat data ** ") - ExaGeoStatData data(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); // Initiating the matrix of the CHAMELEON Descriptor Z. auto *z_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, @@ -77,15 +75,16 @@ int main(int argc, char **argv) { 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, 0.942824444953078489}; - data.GetLocations()->SetLocationX(*location_x, N); - data.GetLocations()->SetLocationY(*location_y, N); + data->GetLocations()->SetLocationX(*location_x, N); + data->GetLocations()->SetLocationY(*location_y, N); - LOGGER("** ExaGeoStat Data Modeling ** ") + // Modeling module. ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data, z_matrix); - LOGGER("** All example stages have been completed successfully ** ") + // Freeing the allocated memory. delete[] z_matrix; delete[] location_x; delete[] location_y; + return 0; } diff --git a/examples/end-to-end/DataPrediction.cpp b/examples/end-to-end/DataPrediction.cpp index 9e51f737..f3bda1cd 100644 --- a/examples/end-to-end/DataPrediction.cpp +++ b/examples/end-to-end/DataPrediction.cpp @@ -44,13 +44,11 @@ int main(int argc, char **argv) { configurations.SetDenseTileSize(dts); // initialize ExaGeoStat hardware with the selected number of cores and gpus. - LOGGER("** Initialise ExaGeoStat hardware **") auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); //Data Setup - LOGGER("** Create ExaGeoStat data **") - ExaGeoStatData data(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); //creating locations x and y. auto *location_x = new double[N]{0.193041886015106440, 0.330556191348134576, 0.181612878614480805, @@ -75,15 +73,15 @@ int main(int argc, char **argv) { 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, 0.290822066007430102}; - data.GetLocations()->SetLocationX(*location_x, N); - data.GetLocations()->SetLocationY(*location_y, N); + data->GetLocations()->SetLocationX(*location_x, N); + data->GetLocations()->SetLocationY(*location_y, N); - LOGGER("** ExaGeoStat data Prediction **") + // Prediction module ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); - LOGGER("** All example stages have been completed successfully **") delete[] location_x; delete[] location_y; delete[] z_matrix; + return 0; } \ No newline at end of file diff --git a/inst/include/api/ExaGeoStat.hpp b/inst/include/api/ExaGeoStat.hpp index 7de91beb..a2aa5191 100644 --- a/inst/include/api/ExaGeoStat.hpp +++ b/inst/include/api/ExaGeoStat.hpp @@ -41,7 +41,7 @@ namespace exageostat::api { */ static void ExaGeoStatLoadData(const hardware::ExaGeoStatHardware &aHardware, configurations::Configurations &aConfigurations, - dataunits::ExaGeoStatData &aData); + std::unique_ptr> &aData); /** * @brief Models Data whether it's synthetic data or real. @@ -54,7 +54,7 @@ namespace exageostat::api { */ static T ExaGeoStatDataModeling(const hardware::ExaGeoStatHardware &aHardware, configurations::Configurations &aConfigurations, - exageostat::dataunits::ExaGeoStatData &aData, + std::unique_ptr> &aData, T *apMeasurementsMatrix = nullptr); @@ -77,7 +77,7 @@ namespace exageostat::api { */ static void ExaGeoStatPrediction(const hardware::ExaGeoStatHardware &aHardware, configurations::Configurations &aConfigurations, - exageostat::dataunits::ExaGeoStatData &aData, + std::unique_ptr> &aData, T *apMeasurementsMatrix = nullptr); private: diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index 406e44c4..184d21a4 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -184,6 +184,9 @@ namespace exageostat::common { DESCRIPTOR_C_DIAG = 49, DESCRIPTOR_A = 50, DESCRIPTOR_RESULTS = 51, + DESCRIPTOR_SUM = 52, + DESCRIPTOR_R = 53, + DESCRIPTOR_R_COPY = 54, }; /** @@ -246,6 +249,16 @@ namespace exageostat::common { EXAGEOSTAT_UPPER_LOWER = 123 /**< Use the full A */ }; + /** + * @enum CopyDirection + * @brief Enum denoting the copy descriptors flow + * + */ + enum CopyDirection : int { + CHAMELEON_TO_HICMA = 0, + HICMA_TO_CHAMELEON = 1 + }; + /** * @var availableKernels * @brief Set denoting the available kernels supported in matrix generation. diff --git a/inst/include/common/PluginRegistry.hpp b/inst/include/common/PluginRegistry.hpp index b0ba4f6e..616909fc 100644 --- a/inst/include/common/PluginRegistry.hpp +++ b/inst/include/common/PluginRegistry.hpp @@ -25,6 +25,8 @@ #include #include +#include + namespace exageostat::plugins { /** * @brief Template class for registering and creating plugins. @@ -68,13 +70,17 @@ namespace exageostat::plugins { * @return A pointer to the created plugin, or nullptr if the plugin could not be created. * */ - static T *Create(const std::string &aName) { + static T *Create(const std::string &aName, const int& aTimeSlot) { auto map = GetFactoryMap(); if (map.find(aName) == map.end()) { return nullptr; } - return map[aName](); + // Get the object from the map. + T* object = map[aName](); + // Automatically set the new P value which will get updated with the user input of P. + object->SetPValue(aTimeSlot); + return object; } private: diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index 4c69a9b1..85a26b3f 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -206,10 +206,6 @@ namespace exageostat::configurations { CREATE_GETTER_FUNCTION(Logger, bool, "Logger") - CREATE_SETTER_FUNCTION(P, int, aP, "P") - - CREATE_GETTER_FUNCTION(P, int, "P") - CREATE_SETTER_FUNCTION(MeanSquareError, double, aMeanSquareError, "MeanSquareError") CREATE_GETTER_FUNCTION(MeanSquareError, double, "MeanSquareError") @@ -230,6 +226,10 @@ namespace exageostat::configurations { CREATE_GETTER_FUNCTION(StartingTheta, std::vector &, "StartingTheta") + CREATE_SETTER_FUNCTION(IsNonGaussian, bool, aIsNonGaussian, "IsNonGaussian") + + CREATE_GETTER_FUNCTION(IsNonGaussian, bool, "IsNonGaussian") + /** * @brief Getter for the verbosity. * @return The verbosity mode. @@ -393,9 +393,6 @@ namespace exageostat::configurations { private: - /// static bool to make sure that print summary is only performed once. - static bool mIsPrinted; - /** * @brief Checks the run mode and sets the verbosity level. * @param[in] aVerbosity A string representing the desired run mode ("verbose" or "standard"). diff --git a/inst/include/data-generators/DataGenerator.hpp b/inst/include/data-generators/DataGenerator.hpp index e0c167cb..121a03ec 100644 --- a/inst/include/data-generators/DataGenerator.hpp +++ b/inst/include/data-generators/DataGenerator.hpp @@ -37,10 +37,11 @@ namespace exageostat::generators { * @details This method generates the X, Y, and Z variables used to define the locations of the data points. * @param[in] aConfigurations Reference to the data configurations. * @param[in] aHardware Reference to the used hardware. - * @return Pointer to a populated data. + * @param[in] aKernel Reference to the used Kernel. + * @return unique Pointer to a populated data. * */ - virtual dataunits::ExaGeoStatData * + virtual std::unique_ptr> CreateData(exageostat::configurations::Configurations &aConfigurations, const exageostat::hardware::ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) = 0; diff --git a/inst/include/data-generators/concrete/CSVDataGenerator.hpp b/inst/include/data-generators/concrete/CSVDataGenerator.hpp index 5ad3e266..e75d6eea 100644 --- a/inst/include/data-generators/concrete/CSVDataGenerator.hpp +++ b/inst/include/data-generators/concrete/CSVDataGenerator.hpp @@ -40,10 +40,9 @@ namespace exageostat::generators::csv { * @copydoc DataGenerator::CreateData() * */ - dataunits::ExaGeoStatData * - CreateData(exageostat::configurations::Configurations &, - const exageostat::hardware::ExaGeoStatHardware &aHardware, - exageostat::kernels::Kernel &aKernel) override; + std::unique_ptr> CreateData(exageostat::configurations::Configurations &, + const exageostat::hardware::ExaGeoStatHardware &aHardware, + exageostat::kernels::Kernel &aKernel) override; /** * @brief Reads CSV files containing 2D, 3D, or ST locations, and measurements vector. @@ -52,12 +51,12 @@ namespace exageostat::generators::csv { * @param aXLocations Reference to the location's x coordinates matrix to be filled with read data. * @param aYLocations Reference to the location's y coordinates matrix to be filled with read data. * @param aZLocations Reference to the location's z coordinates matrix to be filled with read data. + * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ - void - ReadData(exageostat::configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, - std::vector &aXLocations, - std::vector &aYLocations, std::vector &aZLocations); + void ReadData(exageostat::configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, + std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, + const int &aP); /** * @brief Release the singleton instance of the CSVDataGenerator class. diff --git a/inst/include/data-generators/concrete/SyntheticGenerator.hpp b/inst/include/data-generators/concrete/SyntheticGenerator.hpp index a4d0aeae..d08ed76e 100644 --- a/inst/include/data-generators/concrete/SyntheticGenerator.hpp +++ b/inst/include/data-generators/concrete/SyntheticGenerator.hpp @@ -56,7 +56,7 @@ namespace exageostat::generators::synthetic { * @copydoc DataGenerator::CreateData() * */ - dataunits::ExaGeoStatData * + std::unique_ptr> CreateData(exageostat::configurations::Configurations &aConfigurations, const exageostat::hardware::ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) override; @@ -68,7 +68,7 @@ namespace exageostat::generators::synthetic { * @return The scaled uniform distribution between the two bounds. * */ - static double UniformDistribution(const double &aRangeLow, const double &aRangeHigh); + static T UniformDistribution(const T &aRangeLow, const T &aRangeHigh); /** * @brief Sort locations in Morton order (input points must be in [0;1]x[0;1] square]). diff --git a/inst/include/data-units/DescriptorData.hpp b/inst/include/data-units/DescriptorData.hpp index 242f2ec1..467daf72 100644 --- a/inst/include/data-units/DescriptorData.hpp +++ b/inst/include/data-units/DescriptorData.hpp @@ -26,7 +26,7 @@ namespace exageostat::dataunits { */ union BaseDescriptor { CHAM_desc_t *chameleon_desc; -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA HICMA_desc_t *hicma_desc; #endif }; @@ -56,7 +56,7 @@ namespace exageostat::dataunits { * @param[in] aDescriptorType The type of the descriptor. * @param[in] aDescriptorName The name of the descriptor. * @return The base descriptor. - * @throws std::runtime_error if the corresponding library is not enabled (EXAGEOSTAT_USE_CHAMELEON or EXAGEOSTAT_USE_HICMA). + * @throws std::runtime_error if the corresponding library is not enabled (USE_HICMA). */ BaseDescriptor GetDescriptor(const common::DescriptorType &aDescriptorType, const common::DescriptorName &aDescriptorName); @@ -89,7 +89,7 @@ namespace exageostat::dataunits { */ void SetRequest(void *apRequest); -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA /** * @brief Converts a CHAMELEON descriptor to a HICMA descriptor. @@ -118,20 +118,21 @@ namespace exageostat::dataunits { * @param[in] aN The number of columns in the sub-matrix. * @param[in] aP The number of rows in the complete matrix. * @param[in] aQ The number of columns in the complete matrix. + * @param[in] aValidOOC Boolean refer to whether this descriptor can be created with OOC technology or not, default is true * @return void - * @throws std::runtime_error if the corresponding library is not enabled (EXAGEOSTAT_USE_CHAMELEON or EXAGEOSTAT_USE_HICMA). + * @throws std::runtime_error if the corresponding library is not enabled (USE_HICMA). */ void SetDescriptor(const common::DescriptorType &aDescriptorType, const common::DescriptorName &aDescriptorName, const bool &aIsOOC, void *apMatrix, const common::FloatPoint &aFloatPoint, const int &aMB, const int &aNB, const int &aSize, const int &aLM, const int &aLN, const int &aI, - const int &aJ, const int &aM, const int &aN, const int &aP, const int &aQ); + const int &aJ, const int &aM, const int &aN, const int &aP, const int &aQ, const bool &aValidOOC = true); /** * @brief Getter for the Descriptor matrix. * @param[in] aDescriptorType Type of the descriptor, whether it's CHAMELEON or HiCMA. * @param[in] apDescriptor Pointer to the descriptor. * @return pointer to the Descriptor matrix. - * @throws std::runtime_error if the corresponding library is not enabled (EXAGEOSTAT_USE_CHAMELEON or EXAGEOSTAT_USE_HICMA). + * @throws std::runtime_error if the corresponding library is not enabled (USE_HICMA). */ T *GetDescriptorMatrix(const common::DescriptorType &aDescriptorType, void *apDescriptor); diff --git a/inst/include/data-units/ModelingDataHolders.hpp b/inst/include/data-units/ModelingDataHolders.hpp index b0efc0f7..d51c07ae 100644 --- a/inst/include/data-units/ModelingDataHolders.hpp +++ b/inst/include/data-units/ModelingDataHolders.hpp @@ -18,7 +18,7 @@ namespace exageostat::dataunits { template struct mModelingData { /// ExaGeoStatData object containing needed descriptors, and locations. - dataunits::ExaGeoStatData *mpData; + std::unique_ptr> *mpData; /// Configurations object containing user input data. configurations::Configurations *mpConfiguration; /// Hardware configuration for the ExaGeoStat solver. @@ -36,7 +36,7 @@ namespace exageostat::dataunits { * @param aHardware The hardware configuration object. * @param aKernel The Kernel object. */ - mModelingData(dataunits::ExaGeoStatData &aData, configurations::Configurations &aConfiguration, + mModelingData(std::unique_ptr> &aData, configurations::Configurations &aConfiguration, const hardware::ExaGeoStatHardware &aHardware, T &aMatrix, const kernels::Kernel &aKernel) : mpData(std::move(&aData)), mpConfiguration(&aConfiguration), mpHardware(&aHardware), mpMeasurementsMatrix(&aMatrix), mpKernel(&aKernel) {} diff --git a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp index ab82aeb5..17bcc39a 100644 --- a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp +++ b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp @@ -69,13 +69,14 @@ namespace exageostat::dataunits::descriptor { * @param[in] aN The number of columns of the sub-matrix. * @param[in] aP The number of rows of the 2D distribution grid. * @param[in] aQ The number of columns of the 2D distribution grid. + * @param[in] aValidOOC Boolean refer to whether this descriptor can be created with OOC technology or not. * @return A pointer to the newly created descriptor. * */ void *CreateDescriptor(void *apDescriptor, const common::DescriptorType &aDescriptorType, const bool &aIsOOC, void *apMatrix, const common::FloatPoint &aFloatPoint, const int &aMB, const int &aNB, const int &aSize, const int &aLM, const int &aLN, const int &aI, const int &aJ, - const int &aM, const int &aN, const int &aP, const int &aQ); + const int &aM, const int &aN, const int &aP, const int &aQ, const bool &aValidOOC); /** * @brief destroys and finalize a descriptor diff --git a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp index 1d8ce866..a3c233c0 100644 --- a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp @@ -47,6 +47,7 @@ namespace exageostat::dataunits::descriptor { * @param[in] aN The number of columns of the sub-matrix. * @param[in] aP The number of rows of the 2D distribution grid. * @param[in] aQ The number of columns of the 2D distribution grid. + * @param[in] aValidOOC Boolean refer to whether this descriptor can be created with OOC technology or not. * @return A pointer to the newly created CHAM_desc_t descriptor. * */ @@ -54,7 +55,7 @@ namespace exageostat::dataunits::descriptor { const common::FloatPoint &aFloatPoint, const int &aMB, const int &aNB, const int &aSize, const int &aLM, const int &aLN, const int &aI, const int &aJ, const int &aM, const int &aN, - const int &aP, const int &aQ); + const int &aP, const int &aQ, const bool &aValidOOC); /** * @brief destroys and finalize a descriptor diff --git a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp index 7d62663d..e5ab7268 100644 --- a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp @@ -47,6 +47,7 @@ namespace exageostat::dataunits::descriptor { * @param[in] aN The number of columns of the sub-matrix. * @param[in] aP The number of rows of the 2D distribution grid. * @param[in] aQ The number of columns of the 2D distribution grid. + * @param[in] aValidOOC Boolean refer to whether this descriptor can be created with OOC technology or not. * @return A pointer to the newly created HICMA_desc_t descriptor. * */ @@ -54,7 +55,7 @@ namespace exageostat::dataunits::descriptor { const common::FloatPoint &aFloatPoint, const int &aMB, const int &aNB, const int &aSize, const int &aLM, const int &aLN, const int &aI, const int &aJ, const int &aM, const int &aN, - const int &aP, const int &aQ); + const int &aP, const int &aQ, const bool &aValidOOC); /** * @brief destroys and finalize a descriptor diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index a5daf00b..9d9e66db 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -46,7 +46,7 @@ namespace exageostat::hardware { */ [[nodiscard]] void *GetChameleonContext() const; -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA /** * @brief Get the Hicma hardware context. @@ -68,7 +68,7 @@ namespace exageostat::hardware { private: //// Used Pointer to the Chameleon hardware context. void *mpChameleonContext = nullptr; -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA //// Used Pointer to the Hicma hardware context. void *mpHicmaContext = nullptr; #endif diff --git a/inst/include/helpers/DistanceCalculationHelpers.hpp b/inst/include/helpers/DistanceCalculationHelpers.hpp index c8c5e44f..2d68eb7a 100644 --- a/inst/include/helpers/DistanceCalculationHelpers.hpp +++ b/inst/include/helpers/DistanceCalculationHelpers.hpp @@ -52,6 +52,14 @@ namespace exageostat::helpers { */ static T DistanceEarth(T &aLatitude1, T &aLongitude1, T &aLatitude2, T &aLongitude2); + /** + * @brief Converts an angle from degrees to radians. + * @details This function converts an angle from degrees to radians using the conversion factor π/180. + * @param[in] aDegree The angle in degrees. + * @return The angle converted to radians. + */ + static T DegreeToRadian(T aDegree); + }; /** * @brief Instantiates the PredictionHelpers class for float and double types. diff --git a/inst/include/kernels/Kernel.hpp b/inst/include/kernels/Kernel.hpp index 0ee2d1d2..7bde9182 100644 --- a/inst/include/kernels/Kernel.hpp +++ b/inst/include/kernels/Kernel.hpp @@ -140,15 +140,15 @@ namespace exageostat::kernels { * @return The value of P. * */ - [[nodiscard]] int GetPValue() const; + [[nodiscard]] int GetP() const; /** * @brief Sets the value of the parameter P used by the kernel function. - * @param[in] aP Value to set `mP` with. + * @param[in] aTimeSlot Value to set `mP` with. * @return void * */ - void SetPValue(int aP); + void SetPValue(int aTimeSlot); /** * @brief Returns the number of the parameters used by the kernel function. @@ -160,6 +160,8 @@ namespace exageostat::kernels { protected: //// Used P. int mP = 1; + //// Used P multiplied by timeslot + int mCalculatedP = 1; //// Used number of parameters. int mParametersNumber = 3; }; diff --git a/inst/include/kernels/concrete/UnivariateMaternNonStat.hpp b/inst/include/kernels/concrete/UnivariateMaternNonStat.hpp deleted file mode 100644 index 4509c13f..00000000 --- a/inst/include/kernels/concrete/UnivariateMaternNonStat.hpp +++ /dev/null @@ -1,141 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file UnivariateMaternNonStat.hpp - * @brief Defines the UnivariateMaternNonStat class, a Univariate Matern Non Stat kernel. - * @version 1.0.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @author Suhas Shankar - * @author Mary Lai Salvana - * @date 2023-04-14 -**/ - -#ifndef EXAGEOSTATCPP_UNIVARIATEMATERNNONSTAT_HPP -#define EXAGEOSTATCPP_UNIVARIATEMATERNNONSTAT_HPP - -#include - -namespace exageostat::kernels { - - /** - * @class UnivariateMaternNonStat - * @brief A class representing a Univariate Matern Non Stat kernel. - * @details This class represents a Univariate Matern Non Stat,which is a subclass of the Kernel class. - * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. - * - */ - template - class UnivariateMaternNonStat : public Kernel { - - public: - - /** - * @brief Constructs a new UnivariateMaternNonStat object. - * @details Initializes a new UnivariateMaternNonStat object with default values. - */ - UnivariateMaternNonStat(); - - /** - * @brief Virtual destructor to allow calls to the correct concrete destructor. - * - */ - ~UnivariateMaternNonStat() override = default; - - /** - * @brief Generates a covariance matrix using a set of locations and kernel parameters. - * @copydoc Kernel::GenerateCovarianceMatrix() - */ - void - GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, - const int &aColumnOffset, dataunits::Locations &aLocation1, - dataunits::Locations &aLocation2, dataunits::Locations &aLocation3, - T *apLocalTheta, const int &aDistanceMetric) override; - - /** - * @brief Creates a new UnivariateMaternNonStat object. - * @details This method creates a new UnivariateMaternNonStat object and returns a pointer to it. - * @return A pointer to the new UnivariateMaternNonStat object. - * - */ - static Kernel *Create(); - - /** - * Function for smoothness parameter - * @param[in] aX x co-ordinate - * @param[in] aY y co-ordinate - * @param[in] aG parameter for function - * @param[in] aH parameter for function - * @param[in] aTi parameter for function - * @return The function ge^(h(x+y)) + ti - * - */ - static double Neu(double aX, double aY, double aG, double aH, double aTi); - - /** - * Function for partial sill - * @param[in] aX x co-ordinate - * @param[in] aY y co-ordinate - * @param[in] aD parameter for function - * @param[in] aE parameter for function - * @param[in] aF parameter for function - * @return The function de^(e(x+y)) + f - * - */ - static double Sigma(double aX, double aY, double aD, double aE, double aF); - - /** - * Function for spatial range - * @param[in] aX x co-ordinate - * @param[in] aY y co-ordinate - * @param[in] aA parameter for function - * @param[in] aB parameter for function - * @return The function ae^(sin bx + sin by) - * - */ - static double Lambda(double aX, double aY, double aA, double aB); - - /** - * Returns the Mahalanobis distance between two points to account for anisotropy - * @param[in] aX1 x co-ordinate of first point - * @param[in] aY1 y co-ordinate of first point - * @param[in] aX2 x co-ordinate of second point - * @param[in] aY2 y co-ordinate of second point - * @param[in] aA11 First element of the positive definite matrix that defines the Mahalanobis Distance - * @param[in] aA12 Second element of the positive definite matrix that defines the Mahalanobis Distance - * @param[in] aA21 Third element of the positive definite matrix that defines the Mahalanobis Distance - * @param[in] aA22 Fourth element of the positive definite matrix that defines the Mahalanobis Distance - * @return The Mahalanobis Distance - * - */ - static double - CalculateMahalanobisDistanceSquared(double aX1, double aY1, double aX2, double aY2, double aA11, double aA12, - double aA21, double aA22); - - /** - * Utility function that evaluates the matern. Similar to (https://www.rdocumentation.org/packages/PrevMap/versions/1.5.3/topics/matern.kernel) in R - * @param[in] aRange Spatial Range parameter (Also known as rho) - * @param[in] aSmoothness Smoothness parameter (Also known as neu) - * @param[in] aDistance Distance between the two locations - * @return Matern function evaluation - * - */ - static double MaternUtil(double aRange, double aSmoothness, double aDistance); - - private: - //// Used plugin name for static registration - static bool plugin_name; - }; - - /** - * @brief Instantiates the Data Generator class for float and double types. - * @tparam T Data Type: float or double - * - */ - EXAGEOSTAT_INSTANTIATE_CLASS(UnivariateMaternNonStat) -}//namespace exageostat - -#endif //EXAGEOSTATCPP_UNIVARIATEMATERNNONSTAT_HPP diff --git a/inst/include/kernels/concrete/UnivariateMaternNonStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternNonStationary.hpp deleted file mode 100644 index e2422f59..00000000 --- a/inst/include/kernels/concrete/UnivariateMaternNonStationary.hpp +++ /dev/null @@ -1,72 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file UnivariateMaternNonStationary.hpp - * @brief Defines the UnivariateMaternNonStationary class, a Univariate Matern Non Stationary kernel. - * @version 1.0.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @author Suhas Shankar - * @author Mary Lai Salvana - * @date 2023-04-13 -**/ - -#ifndef EXAGEOSTATCPP_UNIVARIATEMATERNNONSTATIONARY_HPP -#define EXAGEOSTATCPP_UNIVARIATEMATERNNONSTATIONARY_HPP - -#include - -namespace exageostat::kernels { - - /** - * @class UnivariateMaternNonStationary - * @brief A class representing a Univariate Matern Non Stationary kernel. - * @details This class represents a Univariate Matern Non Stationary, which is a subclass of the Kernel class. - * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. - * - */ - template - class UnivariateMaternNonStationary : public Kernel { - - public: - - /** - * @brief Constructs a new UnivariateMaternNonStationary object. - * @details Initializes a new UnivariateMaternNonStationary object with default values. - */ - UnivariateMaternNonStationary(); - - /** - * @brief Virtual destructor to allow calls to the correct concrete destructor. - * - */ - ~UnivariateMaternNonStationary() override = default; - - /** - * @brief Generates a covariance matrix using a set of locations and kernel parameters. - * @copydoc Kernel::GenerateCovarianceMatrix() - */ - void - GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, - const int &aColumnOffset, dataunits::Locations &aLocation1, - dataunits::Locations &aLocation2, dataunits::Locations &aLocation3, - T *apLocalTheta, const int &aDistanceMetric) override; - - /** - * @brief Creates a new UnivariateMaternNonStationary object. - * @details This method creates a new UnivariateMaternNonStationary object and returns a pointer to it. - * @return A pointer to the new UnivariateMaternNonStationary object. - * - */ - static Kernel *Create(); - - private: - //// Used plugin name for static registration - static bool plugin_name; - }; -}//namespace exageostat - -#endif //EXAGEOSTATCPP_UNIVARIATEMATERNNONSTATIONARY_HPP diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp index 9790b352..52b77aa6 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp @@ -54,12 +54,14 @@ namespace exageostat::linearAlgebra { * @details This method initializes the descriptors necessary for the linear algebra solver. * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in,out] aDescriptorData Descriptor Data object to be populated with descriptors and data. + * @param[in] aP the P value of the kernel multiplied by time slot. * @param[in] apMeasurementsMatrix Pointer to the measurement matrix. * @return void * */ void InitiateDescriptors(configurations::Configurations &aConfigurations, - dataunits::DescriptorData &aDescriptorData, T *apMeasurementsMatrix = nullptr); + dataunits::DescriptorData &aDescriptorData, + const int &aP, T *apMeasurementsMatrix = nullptr); /** * @brief Initializes the descriptors necessary for the Fisher prediction function. @@ -69,27 +71,29 @@ namespace exageostat::linearAlgebra { void InitiateFisherDescriptors(configurations::Configurations &aConfigurations, dataunits::DescriptorData &aDescriptorData); - /** + /** * @brief Initializes the descriptors necessary for the Prediction. * @details This method initializes the descriptors necessary for the linear algebra solver. * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in,out] aData DescriptorData object to be populated with descriptors and data. + * @param[in] aP the P value of the kernel multiplied by time slot. * @return void * */ void InitiatePredictionDescriptors(configurations::Configurations &aConfigurations, - dataunits::ExaGeoStatData &aData); + std::unique_ptr> &aData, const int &aP); /** * @brief Initializes the descriptors necessary for the Prediction Auxiliary function MLE-MLOE-MMOM. * @details This method initializes the descriptors necessary for the linear algebra solver. * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in,out] aData DescriptorData object to be populated with descriptors and data. + * @param[in] aP the P value of the kernel multiplied by time slot. * @return void * */ void InitiateMLOEMMOMDescriptors(configurations::Configurations &aConfigurations, - dataunits::ExaGeoStatData &aData); + std::unique_ptr> &aData, const int &aP); /** * @brief Generates synthetic data. @@ -101,7 +105,8 @@ namespace exageostat::linearAlgebra { * */ void GenerateSyntheticData(configurations::Configurations &aConfigurations, - const hardware::ExaGeoStatHardware &aHardware, dataunits::ExaGeoStatData &aData, + const hardware::ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, const kernels::Kernel &aKernel); /** @@ -147,7 +152,8 @@ namespace exageostat::linearAlgebra { * */ void - GenerateObservationsVector(configurations::Configurations &aConfigurations, dataunits::ExaGeoStatData &aData, + GenerateObservationsVector(configurations::Configurations &aConfigurations, + std::unique_ptr> &aData, dataunits::Locations *apLocation1, dataunits::Locations *apLocation2, dataunits::Locations *apLocation3, const int &aDistanceMetric, const kernels::Kernel &aKernel); @@ -163,7 +169,8 @@ namespace exageostat::linearAlgebra { * @return log likelihood value * */ - virtual T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, dataunits::ExaGeoStatData &aData, + virtual T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, configurations::Configurations &aConfigurations, const double *apTheta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) = 0; @@ -276,7 +283,7 @@ namespace exageostat::linearAlgebra { ExaGeoStatMLETraceTileAsync(void *apDescA, void *apSequence, void *apRequest, void *apDescNum, void *apDescTrace); - /** + /** * @brief Calculate determinant for triangular matrix. * @param[in] apDescA Exageostat descriptor. * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. @@ -307,7 +314,7 @@ namespace exageostat::linearAlgebra { * @return Returns 0 for success, error code otherwise. */ int ExaGeoStatMLEMSPETileAsync(void *apDescZPredict, void *apDescZMiss, void *apDescError, void *apSequence, - void *apRequest); + void *apRequest); /** * Predict missing values base on a set of given values and covariance matrix/ @@ -325,12 +332,39 @@ namespace exageostat::linearAlgebra { * @param[in] aKernel Reference to the kernel object to use. * @return the prediction Mean Square Error (MSPE). */ - T * ExaGeoStatMLEPredictTile(exageostat::dataunits::ExaGeoStatData &aData, T *apTheta, const int &aZMissNumber, - const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfiguration, - exageostat::dataunits::Locations &aMissLocations, - exageostat::dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); + T *ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, + const int &aZMissNumber, + const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, + const hardware::ExaGeoStatHardware &aHardware, + configurations::Configurations &aConfiguration, + exageostat::dataunits::Locations &aMissLocations, + exageostat::dataunits::Locations &aObsLocations, + const kernels::Kernel &aKernel); + + /** + * Predict missing values base on a set of given values and Non-Gaussian covariance matrix/ + * @param[in] aData Reference to Data containing different MLE inputs. + * @param[in] apTheta theta Vector with three parameter (Variance, Range, Smoothness) that is used to to generate the Covariance Matrix. + * @param[in] aZMissNumber number of missing values (unknown observations). + * @param[in] aZObsNumber number of observed values (known observations). + * @param[in] apZObs observed values vector (known observations). + * @param[in] apZActual actual missing values vector (in the case of testing MSPE). + * @param[in] apZMiss missing values vector (unknown observations). + * @param[in] aHardware ExaGeoStatHardware object representing the hardware. + * @param[in] aConfigurations Configurations object containing relevant settings. + * @param[in] aMissLocations Reference to Locations object containing missed locations. + * @param[in] aObsLocations Reference to Locations object containing observed locations. + * @param[in] aKernel Reference to the kernel object to use. + * @return the prediction Mean Square Error (MSPE). + */ + T *ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, T *apTheta, + const int &aZMissNumber, + const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, + const hardware::ExaGeoStatHardware &aHardware, + configurations::Configurations &aConfiguration, + exageostat::dataunits::Locations &aMissLocations, + exageostat::dataunits::Locations &aObsLocations, + const kernels::Kernel &aKernel); /** * @brief Copy Lapack matrix to Descriptor Matrix. @@ -340,8 +374,7 @@ namespace exageostat::linearAlgebra { * @param[in] aUpperLower Specifies Specifies whether the upper or lower triangular part of the covariance matrix is stored. * @return void */ - virtual void - ExaGeoStatLap2Desc(T *apA, const int &aLDA, void *apDescA, const common::UpperLower &aUpperLower) = 0; + void ExaGeoStatLap2Desc(T *apA, const int &aLDA, void *apDescA, const common::UpperLower &aUpperLower); /** * @brief Copy Descriptor Matrix to Lapack matrix. @@ -353,6 +386,21 @@ namespace exageostat::linearAlgebra { */ void ExaGeoStatDesc2Lap(T *apA, const int &aLDA, void *apDescA, const common::UpperLower &aUpperLower); +#ifdef USE_HICMA + + /** + * @brief Copy Descriptor Matrix to another Descriptor matrix. + * @param[out] apSourceDesc Descriptor matrix to be copied + * @param[out] apDestinationDesc Descriptor matrix to be copied to. + * @param[in] aSize Size of matrix to be copied. + * @param[in] aDirection Specifies the type of Descriptors to be copied. + * @return void + */ + void CopyDescriptors(void *apSourceDesc, void *apDestinationDesc, const int &aSize, + const common::CopyDirection &aDirection); + +#endif + /** * @brief Sets the values of all or part of a two-dimensional Tile. * @param[in] aUpperLower Specifies Specifies whether the upper or lower triangular part of the covariance matrix is stored. @@ -368,9 +416,10 @@ namespace exageostat::linearAlgebra { * @param[out] apZ Pointer to an array to copy Z matrix into. * @param[in] aSize Size of the matrix. * @param[in] aDescData Descriptor data containing required Z matrix Descriptor. + * @param[in] aP the P value of the kernel multiplied by time slot. */ void ExaGeoStatGetZObs(exageostat::configurations::Configurations &aConfigurations, T *apZ, const int &aSize, - exageostat::dataunits::DescriptorData &aDescData, T *apMeasurementsMatrix); + exageostat::dataunits::DescriptorData &aDescData, T *apMeasurementsMatrix, const int &aP); /** * @brief Predict missing values based on a set of given values and covariance matrix. @@ -387,7 +436,7 @@ namespace exageostat::linearAlgebra { * @return void */ void ExaGeoStatMLETileMLOEMMOM(exageostat::configurations::Configurations &aConfigurations, - exageostat::dataunits::ExaGeoStatData &aData, + std::unique_ptr> &aData, const exageostat::hardware::ExaGeoStatHardware &aHardware, T *apTruthTheta, T *apEstimatedTheta, dataunits::Locations &aMissLocations, dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); @@ -402,9 +451,10 @@ namespace exageostat::linearAlgebra { * @return Fisher Matrix */ T * - ExaGeoStatFisherTile(configurations::Configurations &aConfigurations, dataunits::ExaGeoStatData &aData, + ExaGeoStatFisherTile(configurations::Configurations &aConfigurations, + std::unique_ptr> &aData, const hardware::ExaGeoStatHardware &aHardware, T *apTheta, - const kernels::Kernel &aKernel); + const kernels::Kernel &aKernel); /** * @brief Perform an asynchronous computation of MLE, MLOE, and MMOM for a tile. @@ -460,6 +510,47 @@ namespace exageostat::linearAlgebra { */ virtual void *ExaGeoStatDataGetAddr(void *apA, int aAm, int aAn) = 0; + /** + * @brief Calculate mean square error (MSE) scalar value for Bivariate kernels. + * @param[in] apDescZPre Observed measurements descZpre. + * @param[in] apDescZMiss Missing measurements descZpre + * @param[out] apDescsError1 Mean Square Error (MSE) 1. + * @param[out] apDescsError2 Mean Square Error (MSE) 2. + * @param[out] apDescsError Mean Square Error (MSE). + * @param[in] apSequence Sequence for the computation. + * @param[in] apRequest Request for the computation. + * @return successful exit + */ + int ExaGeoStatMLEMSPEBivariateTileAsync(void *apDescZPre, void *apDescZMiss, void *apDescsError1, + void *apDescsError2, void *apDescsError, + void *apSequence, void *apRequest); + + /** + * @brief Transform the measurements vector inside the non-Gaussian MLE function. + * @param[in] apDescZ pointer to the Observed Measurements descriptor. + * @param[in] apDescFlag Pointer to flag descriptor. + * @param[in] apTheta Pointer to Model parameters. + * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. + * @param[in] apRequest Identifies this function call (for exception handling purposes). + * @return Returns 0 for success, error code otherwise. + */ + virtual int + ExaGeoStatNonGaussianTransformTileAsync(void *apDescZ, void *apDescFlag, const T *apTheta, void *apSequence, + void *apRequest) = 0; + + /** + * @brief Calculate the log likelihood of non-Gaussian MLE. + * @param[in] apDescZ pointer to the Observed Measurements descriptor. + * @param[in] apDescSum The log-likelihood Sum of descriptor Z. + * @param[in] apTheta Pointer to Model parameters. + * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. + * @param[out] apRequest Identifies this function call (for exception handling purposes). + * @return Returns 0 for success, error code otherwise. + */ + virtual int + ExaGeoStatNonGaussianLogLikeTileAsync(void *apDescZ, void *apDescSum, const T *apTheta, void *apSequence, + void *apRequest) = 0; + /** * @brief Sets the context. * @param[in] apContext The context. @@ -469,7 +560,7 @@ namespace exageostat::linearAlgebra { this->mpContext = apContext; } - //// TODO: Create a Factory for Runtime system. This is a solution fix for now + //// TODO: Create a Factory for Runtime system. HiCMAPP Model will be applied. struct starpu_codelet cl_gaussian_to_non = { .where = STARPU_CPU, @@ -544,133 +635,143 @@ namespace exageostat::linearAlgebra { struct starpu_codelet cl_dtrace = { - .where = STARPU_CPU, - .cpu_funcs = {CORE_dtrace_starpu}, - .nbuffers = 3, - .modes = {STARPU_R, STARPU_RW, STARPU_W}, - .name = "dtrace" + .where = STARPU_CPU, + .cpu_funcs = {CORE_dtrace_starpu}, + .nbuffers = 3, + .modes = {STARPU_R, STARPU_RW, STARPU_W}, + .name = "dtrace" }; struct starpu_codelet cl_ddotp = { - .where = STARPU_CPU, - .cpu_funcs = {CORE_ddotp_starpu}, - .nbuffers = 2, - .modes = {STARPU_RW,STARPU_R}, - .name = "ddotp" + .where = STARPU_CPU, + .cpu_funcs = {CORE_ddotp_starpu}, + .nbuffers = 2, + .modes = {STARPU_RW, STARPU_R}, + .name = "ddotp" + }; + + struct starpu_codelet cl_dmse_bivariate = + { + .where = STARPU_CPU, + .cpu_funcs = {CORE_dmse_bivariate_starpu}, + .nbuffers = 5, + .modes = {STARPU_RW, STARPU_RW, STARPU_RW, STARPU_R, STARPU_R}, + .name = "dmse_bivariate" + }; + struct starpu_codelet cl_non_gaussian_transform = + { + .where = STARPU_CPU, + .cpu_funcs = {CORE_non_gaussian_transform_starpu}, + .nbuffers = 2, + .modes = {STARPU_RW, STARPU_W}, + .name = "non_gaussian_transform" }; + struct starpu_codelet cl_non_gaussian_loglike = + { + .where = STARPU_CPU, + .cpu_funcs = {CORE_non_gaussian_loglike_starpu}, + .nbuffers = 2, + .modes = {STARPU_R, STARPU_RW}, //Read access to Z and Read/Write access to the sum. + .name = "non_gaussian_loglike" + }; + struct starpu_codelet cl_non_gaussian_loglike_lr = + { + .where = STARPU_CPU, + .cpu_funcs = {CORE_non_gaussian_loglike_lr_starpu}, + .nbuffers = 2, + .modes = {STARPU_R, STARPU_RW}, //Read access to Z and Read/Write access to the sum. + .name = "non_gaussian_loglike_lr" + }; + + struct starpu_codelet cl_non_gaussian_transform_lr = + { + .where = STARPU_CPU, + .cpu_funcs = {CORE_non_gaussian_transform_lr_starpu}, + .nbuffers = 1, + .modes = {STARPU_RW}, + .name = "non_gaussian_transform_lr" + }; static void CORE_dcmg_starpu(void *apBuffers[], void *apCodeletArguments) { int m, n, m0, n0; exageostat::dataunits::Locations *location1; exageostat::dataunits::Locations *location2; exageostat::dataunits::Locations *location3; - T *theta; - T *A; + T *pTheta; + T *pA; int distance_metric; - kernels::Kernel *kernel; + kernels::Kernel *pKernel; - A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - starpu_codelet_unpack_args(apCodeletArguments, &m, &n, &m0, &n0, &location1, &location2, &location3, &theta, - &distance_metric, &kernel); - kernel->GenerateCovarianceMatrix(A, m, n, m0, n0, *location1, *location2, *location3, theta, - distance_metric); + pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + starpu_codelet_unpack_args(apCodeletArguments, &m, &n, &m0, &n0, &location1, &location2, &location3, + &pTheta, &distance_metric, &pKernel); + pKernel->GenerateCovarianceMatrix(pA, m, n, m0, n0, *location1, *location2, *location3, pTheta, + distance_metric); } static void CORE_gaussian_to_non_starpu(void *apBuffers[], void *apCodeletArguments) { int m, m0; - T *z; - T *theta; - theta = new T[6]; - z = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + T *pZ; + T *pTheta; + pTheta = new T[6]; + pZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, &theta[0], &theta[1], &theta[2], &theta[3], - &theta[4], &theta[5]); + starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, &pTheta[0], &pTheta[1], &pTheta[2], &pTheta[3], + &pTheta[4], &pTheta[5]); //core function to convert Z tile from Gaussian to non-Gaussian. - core_gaussian_to_non(z, theta, m); - delete[] theta; - } - - static void core_gaussian_to_non(T *Z, T *apLocalTheta, int aSize) { - - double xi = apLocalTheta[2]; - double omega = apLocalTheta[3]; - double g = apLocalTheta[4]; - double h = apLocalTheta[5]; - - int i; - if (h < 0) { - LOGGER("The kurtosis parameter cannot be negative") - return; - } - if (g == 0) { - for (i = 0; i < aSize; i++) - Z[i] = xi + omega * Z[i] * (exp(0.5 * h * pow(Z[i], 2))); - } else { - for (i = 0; i < aSize; i++) - Z[i] = xi + omega * (exp(g * Z[i]) - 1) * (exp(0.5 * h * pow(Z[i], 2))) / g; - } + core_gaussian_to_non(pZ, pTheta, m); + delete[] pTheta; } static void CORE_dzcpy_Starpu(void *apBuffers[], void *apCodeletArguments) { int m; - T *A; + T *pA; int m0; - T *r; + T *pR; - A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, &r); - memcpy(A, &r[m0], m * sizeof(T)); + pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, &pR); + memcpy(pA, &pR[m0], m * sizeof(T)); } static void CORE_dmdet_starpu(void *apBuffers[], void *apCodeletArguments) { int m; int n; - T *A; + T *pA; int m0; int n0; T det = 0; - T *determinant = &det; + T *pDeterminant = &det; - *determinant = 0; - A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - determinant = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + *pDeterminant = 0; + pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pDeterminant = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); starpu_codelet_unpack_args(apCodeletArguments, &m, &n, &m0, &n0); - T local_det = Core_dmdet(A, m); - *determinant += local_det; - } - - static T Core_dmdet(T *apA, int aSize) { - - int i; - T res = 0.0; - for (i = 0; i < aSize; i++) { - if (apA[i + i * aSize] > 0) - res += log(apA[i + i * aSize]); - } - return res; + T local_det = Core_dmdet(pA, m); + *pDeterminant += local_det; } static void CORE_stride_vector_starpu(void *apBuffers[], void *apCodeletArguments) { int m; int temp; - T *A; - T *B; - T *C; + T *pA; + T *pB; + T *pC; int m0; int i; int j = 0; - A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - B = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - C = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pB = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pC = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); starpu_codelet_unpack_args(apCodeletArguments, &temp, &m0, &m); for (i = 0; i < temp - 1; i += 2) { - B[j] = A[i]; - C[j] = A[i + 1]; + pB[j] = pA[i]; + pC[j] = pA[i + 1]; j++; } } @@ -678,41 +779,41 @@ namespace exageostat::linearAlgebra { static void CORE_tri_stride_vector_starpu(void *apBuffers[], void *apCodeletArguments) { int m; int temp; - double *A; - double *B; - double *C; - double *D; + T *pA; + T *pB; + T *pC; + T *pD; int m0; int i; int j; - A = (double *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - B = (double *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - C = (double *) STARPU_MATRIX_GET_PTR(apBuffers[2]); - D = (double *) STARPU_MATRIX_GET_PTR(apBuffers[3]); + pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pB = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pC = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + pD = (T *) STARPU_MATRIX_GET_PTR(apBuffers[3]); starpu_codelet_unpack_args(apCodeletArguments, &temp, &m0, &m); //accept only temp divided by three (should be optimized) j = 0; for (i = 0; i < temp - 1; i += 3) { - B[j] = A[i]; - C[j] = A[i + 1]; - D[j] = A[i + 2]; + pB[j] = pA[i]; + pC[j] = pA[i + 1]; + pD[j] = pA[i + 2]; j++; } } static void CORE_dmse_starpu(void *apBuffers[], void *apCodeletArguments) { int m, m0, i; - double *pZPredict; - double *pZMiss; - double *pError; - double local_error = 0.0; + T *pZPredict; + T *pZMiss; + T *pError; + T local_error = 0.0; - pError = (double *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pZPredict = (double *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - pZMiss = (double *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + pError = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pZPredict = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pZMiss = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); starpu_codelet_unpack_args(apCodeletArguments, &m, &m0); for (i = 0; i < m; i++) { @@ -725,78 +826,284 @@ namespace exageostat::linearAlgebra { int m; int n; int i; - double *expr2; - double *expr3; - double *expr4; - double *mloe; - double *mmom; + T *pExpr2; + T *pExpr3; + T *pExpr4; + T *pMloe; + T *pMmom; int m0; int n0; - expr2 = (double *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - expr3 = (double *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - expr4 = (double *) STARPU_MATRIX_GET_PTR(apBuffers[2]); - mloe = (double *) STARPU_MATRIX_GET_PTR(apBuffers[3]); - mmom = (double *) STARPU_MATRIX_GET_PTR(apBuffers[4]); + pExpr2 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pExpr3 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pExpr4 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + pMloe = (T *) STARPU_MATRIX_GET_PTR(apBuffers[3]); + pMmom = (T *) STARPU_MATRIX_GET_PTR(apBuffers[4]); + starpu_codelet_unpack_args(apCodeletArguments, &m, &n, &m0, &n0); - double expr2_ = 0, expr3_ = 0, expr4_ = 0; + T expr2_ = 0, expr3_ = 0, expr4_ = 0; + for (i = 0; i < m * n; i += 2) { - expr2_ += expr2[i]; - expr3_ += expr3[i]; - expr4_ += expr4[i]; + expr2_ += pExpr2[i]; + expr3_ += pExpr3[i]; + expr4_ += pExpr4[i]; } if (expr3_ == 0.0) { - *mloe -= 1.0; + *pMloe -= 1.0; } else { - *mloe += (expr2_ / expr3_) - 1.0; + *pMloe += (expr2_ / expr3_) - 1.0; } if (expr2_ == 0.0) { - *mmom -= 1.0; + *pMmom -= 1.0; } else { - *mmom += (expr4_ / expr2_) - 1.0; + *pMmom += (expr4_ / expr2_) - 1.0; } } - static void CORE_dtrace_starpu(void *buffers[], void *cl_arg) { + static void CORE_dtrace_starpu(void *apBuffers[], void *apCodeletArguments) { + int m; - double *A; - double s = 0; - double *sum = &s; - double *trace; - - *sum = 0; - A = (double *) STARPU_MATRIX_GET_PTR(buffers[0]); - sum = (double *) STARPU_MATRIX_GET_PTR(buffers[1]); - trace = (double *) STARPU_MATRIX_GET_PTR(buffers[2]); - starpu_codelet_unpack_args(cl_arg, &m); - - double local_s = core_dtrace(A, m, trace); - *sum+= local_s; + T *pA; + T s = 0; + T *pSum = &s; + T *pTrace; + + *pSum = 0; + pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pSum = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pTrace = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + starpu_codelet_unpack_args(apCodeletArguments, &m); + + T local_s = core_dtrace(pA, m, pTrace); + *pSum += local_s; + } + + static void CORE_ddotp_starpu(void *apBuffers[], void *apCodeletArguments) { + int m, m0; + T *pA; + T *pDot_product; + + pDot_product = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + starpu_codelet_unpack_args(apCodeletArguments, &m, &m0); + T local_dot = cblas_ddot(m, (double *) pA, 1, (double *) pA, 1); + *pDot_product += local_dot; + } + + static void CORE_non_gaussian_transform_starpu(void *apBuffers[], void *apCodeletArguments) { + int m, m0; + T *pZ; + T *pTheta; + T flag = 0; + T *pAll_flags = &flag; + *pAll_flags = 0; + + pTheta = new T[6]; + pZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + + starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, /*&flag,*/ + &pTheta[0], &pTheta[1], &pTheta[2], + &pTheta[3], &pTheta[4], &pTheta[5]); + + //Transform the measurements vector inside the non-Gaussian MLE function. + core_non_gaussian_transform(pZ, pTheta, m); + delete[] pTheta; + } + + static void CORE_non_gaussian_loglike_starpu(void *apBuffers[], void *apCodeletArguments) { + int m, m0; + T *pZ; + T sum = 0; + T *pA = ∑ + + *pA = 0; + auto *pTheta = new T[6]; + pZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + + starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, + &pTheta[0], &pTheta[1], &pTheta[2], + &pTheta[3], &pTheta[4], &pTheta[5]); + + T local_sum = core_non_gaussian_loglike(pZ, pTheta, m); + *pA += local_sum; + delete[] pTheta; } - static double core_dtrace (double *A, int m, double* trace) { + static void CORE_non_gaussian_loglike_lr_starpu(void *apBuffers[], void *apCodeletArguments) { + int m, m0; + T *pZ; + T *pTheta; + T sum; + T *pS; + + pTheta = (T *) new T[6]; + pZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pS = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + + starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, + &pTheta[0], &pTheta[1], &pTheta[2], + &pTheta[3], &pTheta[4], &pTheta[5]); + + T local_sum = core_non_gaussian_loglike(pZ, pTheta, m); + *pS += local_sum; + } + + static void CORE_non_gaussian_transform_lr_starpu(void *apBuffers[], void *apCodeletArguments) { + int m, m0; + T *pZ; + T *pTheta; + T flag = 0; + T *pAll_flags = &flag; + *pAll_flags = 0; + + pTheta = (T *) new T[6]; + pZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, /*&flag,*/ + &pTheta[0], &pTheta[1], &pTheta[2], + &pTheta[3], &pTheta[4], &pTheta[5]); + + //Transform the measurements vector inside the non-Gaussian MLE function. + core_non_gaussian_transform(pZ, pTheta, m); + } + + static void core_gaussian_to_non(T *pZ, T *apLocalTheta, int aSize) { + + T xi = apLocalTheta[2]; + T omega = apLocalTheta[3]; + T g = apLocalTheta[4]; + T h = apLocalTheta[5]; + int i; - double res = 0.0; - for (i = 0; i < m; i++) - { - res += A[i + i * m]; - trace[i] = A[i + i * m]; + if (h < 0) { + throw std::runtime_error("The kurtosis parameter cannot be negative"); + } + if (g == 0) { + for (i = 0; i < aSize; i++) + pZ[i] = xi + omega * pZ[i] * (exp(0.5 * h * pow(pZ[i], 2))); + } else { + for (i = 0; i < aSize; i++) + pZ[i] = xi + omega * (exp(g * pZ[i]) - 1) * (exp(0.5 * h * pow(pZ[i], 2))) / g; + } + } + + static T Core_dmdet(T *apA, int aSize) { + + int i; + T res = 0.0; + for (i = 0; i < aSize; i++) { + if (apA[i + i * aSize] > 0) + res += log(apA[i + i * aSize]); } return res; } - static void CORE_ddotp_starpu(void *buffers[], void *cl_arg){ - int m, m0; - double * A; - double * dotproduct; - - dotproduct = (double *)STARPU_MATRIX_GET_PTR(buffers[0]); - A = (double *)STARPU_MATRIX_GET_PTR(buffers[1]); - starpu_codelet_unpack_args(cl_arg, &m, &m0); - double local_dot=cblas_ddot(m, A, 1, A, 1); - *dotproduct += local_dot; + static double core_dtrace(T *pA, int m, T *pTrace) { + int i; + T res = 0.0; + for (i = 0; i < m; i++) { + res += pA[i + i * m]; + pTrace[i] = pA[i + i * m]; + } + return res; + } + + static double tukeyGHTransfor(T aZ_non, T aZ, T aXi, T aOmega, T aG, T aH) { + if (aG == 0) + return aZ_non - aXi - aOmega * aZ * exp(0.5 * aH * aZ * aZ); + else + return aZ_non - aXi - (aOmega * (exp(aG * aZ) - 1) * (exp(0.5 * aH * aZ * aZ)) / aG); + } + + static double tukeyGHDiferencial(T aZ, T aOmega, T aG, T aH) { + if (aG == 0) + return -aOmega * exp((aH * aZ * aZ) / 2.0) - aOmega * aH * aZ * aZ * exp((aH * aZ * aZ) / 2.0); + else + return -aOmega * exp(aG * aZ) * exp((aH * aZ * aZ) / 2.0) - + (aH * aZ * exp((aH * aZ * aZ) / 2.0) * (aOmega * exp(aG * aZ) - aOmega)) / aG; + } + + static double newton_raphson(T aZ, T aXi, T aOmega, T aG, T aH, T aEps) { + int itr, max_itr; + T x0, x1, all_err; + x0 = 0; + T diff; + all_err = aEps; + max_itr = 1000; + for (itr = 1; itr <= max_itr; itr++) { + diff = tukeyGHTransfor(aZ, x0, aXi, aOmega, aG, aH) / tukeyGHDiferencial(x0, aOmega, aG, aH); + x1 = x0 - diff; + if (fabs(diff) < all_err) + return x1; + x0 = x1; + } + + return x1; + } + + static void core_non_gaussian_transform(T *apZ, T *apLocalTheta, int m) { + + T xi = apLocalTheta[2]; + T omega = apLocalTheta[3]; + T g = apLocalTheta[4]; + T h = apLocalTheta[5]; + + T eps = 1.0e-5; + for (int i = 0; i < m; i++) + apZ[i] = newton_raphson(apZ[i], xi, omega, g, h, eps); + } + + static double core_non_gaussian_loglike(T *apZ, T *apLocalTheta, int m) { + T g = apLocalTheta[4]; + T h = apLocalTheta[5]; + + int i; + T sum = 0.0; + if (h < 0) { + throw std::runtime_error("The kurtosis parameter cannot be negative"); + + } + for (i = 0; i < m; i++) { + if (g == 0) + sum += log(1 + h * pow(apZ[i], 2)) + 0.5 * h * pow(apZ[i], 2); + else { + sum += log(exp(g * apZ[i]) + (exp(g * apZ[i]) - 1) * h * apZ[i] / g) + 0.5 * h * pow(apZ[i], 2); + } + } + return sum; + } + + static void CORE_dmse_bivariate_starpu(void *apBuffers[], void *apCodeletArguments) { + int m, m0, i; + T *pZpre; + T *pZmiss; + T *pSerror1; + T *pSerror2; + T *pSerror; + T local_serror1 = 0.0; + T local_serror2 = 0.0; + T local_serror = 0.0; + + pSerror1 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pSerror2 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pSerror = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + pZpre = (T *) STARPU_MATRIX_GET_PTR(apBuffers[3]); + pZmiss = (T *) STARPU_MATRIX_GET_PTR(apBuffers[4]); + + starpu_codelet_unpack_args(apCodeletArguments, &m, &m0); + + for (i = 0; i < m; i++) { + if (i % 2 == 0) { + local_serror1 += pow((pZpre[i] - pZmiss[i]), 2); + } else + local_serror2 += pow((pZpre[i] - pZmiss[i]), 2); + local_serror += pow((pZpre[i] - pZmiss[i]), 2); + } + *pSerror1 += local_serror1; + *pSerror2 += local_serror2; + *pSerror += local_serror; } bool recover(char *apPath, int aIterationCount, T *apTheta, T *apLogLik, int aNumParams) { @@ -809,8 +1116,7 @@ namespace exageostat::linearAlgebra { char *pch; fp = fopen(apPath, "r"); if (fp == nullptr) { - LOGGER("cannot open observations file\n") - exit(EXIT_FAILURE); + throw std::runtime_error("Cannot open observations file"); } while (getline(&line, &len, fp) != -1) { pch = strtok(line, " "); diff --git a/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp b/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp index 159c9c87..26a40ab4 100644 --- a/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp +++ b/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp @@ -9,7 +9,7 @@ #ifndef EXAGEOSTATCPP_HICMAHEADERS_HPP #define EXAGEOSTATCPP_HICMAHEADERS_HPP -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA extern "C" { #include #include diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp index 99ebf86e..6c2d5c61 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp @@ -33,16 +33,10 @@ namespace exageostat::linearAlgebra { * @brief Calculates the log likelihood value of a given value theta. * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() */ - T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, dataunits::ExaGeoStatData &aData, + T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, configurations::Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) override; - /** - * @brief Copy Lapack matrix to Descriptor Matrix - * @copydoc LinearAlgebraMethods::ExaGeoStatLap2Desc() - */ - void ExaGeoStatLap2Desc(T *apA, const int &aLDA, void *apDescA, const common::UpperLower &aUpperLower) override; - /** * @brief Copies a matrix in the tile layout from source to destination * @copydoc LinearAlgebraMethods::ExaGeoStatLapackCopyTile() @@ -143,6 +137,22 @@ namespace exageostat::linearAlgebra { int ExaGeoStatDoubleDotProduct(void *apDescA, void *apDescProduct, void *apSequence, void *apRequest); + /** + * @brief Calculate the loglikelihood of non-Gaussian MLE. + * @copydoc LinearAlgebraMethods::ExaGeoStatNonGaussianLogLikeTileAsync() + */ + int + ExaGeoStatNonGaussianLogLikeTileAsync(void *apDescZ, void *apDescSum, const T *apTheta, void *apSequence, + void *apRequest) override; + + /** + * @brief Calculate the loglikelihood of non-Gaussian MLE. + * @copydoc LinearAlgebraMethods::ExaGeoStatNonGaussianLogLikeTileAsync() + */ + int + ExaGeoStatNonGaussianTransformTileAsync(void *apDescZ, void *apDescFlag, const T *apTheta, void *apSequence, + void *apRequest) override; + }; EXAGEOSTAT_INSTANTIATE_CLASS(ChameleonImplementation) diff --git a/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp index 4520a807..e83f3970 100644 --- a/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp @@ -43,15 +43,17 @@ namespace exageostat::linearAlgebra::tileLowRank { * @brief Set the modeling descriptors for HiCMA implementation. * @param[in,out] aData Reference to the ExaGeoStatData object. * @param[in] aConfigurations Reference to the Configurations object. + * @param[in] aP the P value of the kernel multiplied by time slot. */ - void - SetModelingDescriptors(dataunits::ExaGeoStatData &aData, configurations::Configurations &aConfigurations); + void SetModelingDescriptors(std::unique_ptr> &aData, + configurations::Configurations &aConfigurations, const int &aP); /** * @brief Calculates the log likelihood value of a given value theta. * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() */ - T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &apHardware, dataunits::ExaGeoStatData &aData, + T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &apHardware, + std::unique_ptr> &aData, configurations::Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) override; @@ -115,17 +117,25 @@ namespace exageostat::linearAlgebra::tileLowRank { */ int ExaGeoStatMeasureDetTileAsync(void *apDescA, void *apSequence, void *apRequest, void *apDescDet) override; - /** - * @brief Copy Lapack matrix to Descriptor Matrix - * @copydoc LinearAlgebraMethods::ExaGeoStatLap2Desc() - */ - void ExaGeoStatLap2Desc(T *apA, const int &aLDA, void *apDescA, const common::UpperLower &aUpperLower) override; - /** * @brief Get the pointer to the data or the runtime handler associated to the piece of data (m, n) in desc. * @copydoc LinearAlgebraMethods::ExaGeoStatDataGetAddr() */ void *ExaGeoStatDataGetAddr(void *apA, int aAm, int aAn) override; + + /** + * @brief Calculate the loglikelihood of non-Gaussian MLE. + * @copydoc LinearAlgebraMethods::ExaGeoStatNonGaussianLogLikeTileAsync() + */ + int ExaGeoStatNonGaussianLogLikeTileAsync(void *apDescZ, void *apDescSum, const T *apTheta, + void *apSequence, void *apRequest) override; + + /** + * @brief Calculate the loglikelihood of non-Gaussian MLE. + * @copydoc LinearAlgebraMethods::ExaGeoStatNonGaussianTransformTileAsync() + */ + int ExaGeoStatNonGaussianTransformTileAsync(void *apDescZ, void *apDescFlag, const T *apTheta, + void *apSequence, void *apRequest) override; }; /** diff --git a/inst/include/prediction/Prediction.hpp b/inst/include/prediction/Prediction.hpp index 87b33f16..31ba3453 100644 --- a/inst/include/prediction/Prediction.hpp +++ b/inst/include/prediction/Prediction.hpp @@ -49,7 +49,7 @@ namespace exageostat::prediction { * @return */ void PredictMissingData(const exageostat::hardware::ExaGeoStatHardware &aHardware, - exageostat::dataunits::ExaGeoStatData &aData, + std::unique_ptr> &aData, exageostat::configurations::Configurations &aConfigurations, T *apMeasurementsMatrix, const kernels::Kernel &aKernel); @@ -63,13 +63,15 @@ namespace exageostat::prediction { * @param[out] aMissLocation Location object to be filled with missed locations. * @param[out] aObsLocation Location object to be filled with missed locations. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. + * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ void InitializePredictionArguments(exageostat::configurations::Configurations &aConfigurations, - exageostat::dataunits::ExaGeoStatData &aData, + std::unique_ptr> &aData, std::unique_ptr> &aLinearAlgebraSolver, T *apZObs, T *apZActual, exageostat::dataunits::Locations &aMissLocation, - exageostat::dataunits::Locations &aObsLocation, T *apMeasurementsMatrix); + exageostat::dataunits::Locations &aObsLocation, T *apMeasurementsMatrix, + const int &aP); }; diff --git a/inst/include/prediction/PredictionHelpers.hpp b/inst/include/prediction/PredictionHelpers.hpp index 7955357e..1f84eda6 100644 --- a/inst/include/prediction/PredictionHelpers.hpp +++ b/inst/include/prediction/PredictionHelpers.hpp @@ -38,12 +38,13 @@ namespace exageostat::prediction { * @param[in] apZ Pointer to a copy of the measurements matrix. * @param[out] aMissLocation Location object to be filled with missed locations. * @param[out] aObsLocation Location object to be filled with missed locations. + * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ static void PickRandomPoints(exageostat::configurations::Configurations &aConfigurations, - exageostat::dataunits::ExaGeoStatData &aData, T *apZObs, T *apZActual, T *apZ, + std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, exageostat::dataunits::Locations &aMissLocation, - exageostat::dataunits::Locations &aObsLocation); + exageostat::dataunits::Locations &aObsLocation, const int &aP); /** * @brief Shuffle array. diff --git a/package.pc.in b/package.pc.in index 014e8173..86ccc3b5 100644 --- a/package.pc.in +++ b/package.pc.in @@ -8,4 +8,4 @@ Description: @CMAKE_PROJECT_DESCRIPTION@ Version: @PROJECT_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -l@PROJECT_NAME@ -URL: http://github.com/ecrc/exageostat-cpp \ No newline at end of file +URL: https://github.com/ecrc/ExaGeoStatCPP \ No newline at end of file diff --git a/scripts/Benchmarking.sh b/scripts/Benchmarking.sh new file mode 100644 index 00000000..1bd12b0b --- /dev/null +++ b/scripts/Benchmarking.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file config.sh +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2023-10-10 + +# Set the output file name +output_file="VULTURE_40CORE_EXACT.csv" + +# Create or truncate the output file +echo "N,computation,kernel,ncores,ngpus,dts,Zmiss,maximum_likelihood,maximum_theta,mspe_value,time_generation,time_modeling,time_prediction,time_modeling_per_iter,Gflops_data_generation,Gflops_data_modeling,Gflops_data_modeling_iter,Gflops_data_mspe" > "$output_file" + +CORES=40 +GPUS=0 +MLE_ITERATIONS=1 +DTS=960 + +# Define the desired values for N +desired_N=(55225 63001 71289 79524 87616 96100 104329 112225 120081 130889 150000 200000) + +# Loop over N values +for N in "${desired_N[@]}"; do + # Repeat each iteration 10 times + for iteration in {1..10}; do + # Run the command and capture the output + command_output=$(./bin/examples/end-to-end/Example_Data_Generation_Modeling_and_Prediction --ncores=$CORES --gpus=$GPUS --computation=exact --itheta=1:0.1:0.5 --etheta=1:0.1:\? --olb=0.1:0.1:0.1 --oub=5:5:5 --dts="$DTS" --verbose=detailed --N="$N" --max_mle_iterations=$MLE_ITERATIONS --kernel=univariate_matern_stationary --tolerance=4 --Zmiss="$(($N/10))" --mspe) + # Extract the desired values from the command output + COMPUTATION=$(echo "$command_output" | awk -F "#Computation:" '{print $2}' | awk -F "," '{print $1}') + COMPUTATION=$(echo "$COMPUTATION" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + KERNEL=$(echo "$command_output" | awk -F "#Kernel:" '{print $2}' | awk -F "," '{print $1}') + KERNEL=$(echo "$KERNEL" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + logli_result=$(echo "$command_output" | awk -F "#Final Log Likelihood value: " '{print $2}' | awk -F "," '{print $1}') + logli_result=$(echo "$logli_result" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + maximum_theta=$(echo "$command_output" | awk -F "#Found Maximum Theta at: " '{print $2}' | awk -F "," '{print $1}') + maximum_theta=$(echo "$maximum_theta" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + mspe_value=$(echo "$command_output" | awk -F "#Mean Square Error MSPE: " '{print $2}' | awk -F "," '{print $1}') + mspe_value=$(echo "$mspe_value" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + time_generation=$(echo "$command_output" | awk -F "#Total Data Generation Execution Time: " '{print $2}' | awk -F "," '{print $1}') + time_generation=$(echo "$time_generation" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + time_modeling=$(echo "$command_output" | awk -F "#Total MLE Execution time: " '{print $2}' | awk -F "," '{print $1}') + time_modeling=$(echo "$time_modeling" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + time_prediction=$(echo "$command_output" | awk -F "#MSPE Prediction Execution Time: " '{print $2}' | awk -F "," '{print $1}') + time_prediction=$(echo "$time_prediction" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + time_modeling_iteration=$(echo "$command_output" | awk -F "#Average Time Modeling per Iteration: " '{print $2}' | awk -F "," '{print $1}') + time_modeling_iteration=$(echo "$time_modeling_iteration" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + flops_generation=$(echo "$command_output" | awk -F "#Total Data Generation Gflop/s: " '{print $2}' | awk -F "," '{print $1}') + flops_generation=$(echo "$flops_generation" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + flops_modeling=$(echo "$command_output" | awk -F "#Total MLE Gflop/s: " '{print $2}' | awk -F "," '{print $1}') + flops_modeling=$(echo "$flops_modeling" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + flops_modeling_iteration=$(echo "$command_output" | awk -F "#Average Flops per Iteration: " '{print $2}' | awk -F "," '{print $1}') + flops_modeling_iteration=$(echo "$flops_modeling_iteration" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + flops_mspe=$(echo "$command_output" | awk -F "#MSPE Gflop/s: " '{print $2}' | awk -F "," '{print $1}') + flops_mspe=$(echo "$flops_mspe" | tr -d '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + + echo "$command_output" + echo "Iteration number $iteration done." + # Append the values to the output file + echo '**Results**' + echo "$N,$COMPUTATION,$KERNEL,$CORES,$GPUS,$DTS,$(($N/10)),$logli_result,$maximum_theta,$mspe_value,$time_generation,$time_modeling,$time_prediction,$time_modeling_iteration,$flops_generation,$flops_modeling,$flops_modeling_iteration,$flops_mspe" + echo '' + echo "$N,$COMPUTATION,$KERNEL,$CORES,$GPUS,$DTS,$(($N/10)),$logli_result,$maximum_theta,$mspe_value,$time_generation,$time_modeling,$time_prediction,$time_modeling_iteration,$flops_generation,$flops_modeling,$flops_modeling_iteration,$flops_mspe" >> "$output_file" + done +done diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 94d4645f..0cba4556 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,7 +46,8 @@ target_include_directories(${LIB_NAME} ) install(TARGETS ${LIB_NAME} EXPORT ${LIB_NAME}CoreConfig - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib + LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib + ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib ) -install(EXPORT ${LIB_NAME}CoreConfig DESTINATION lib/cmake/${PROJECT_NAME}) \ No newline at end of file + + install(EXPORT ${LIB_NAME}CoreConfig DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/cmake/) \ No newline at end of file diff --git a/src/api/ExaGeoStat.cpp b/src/api/ExaGeoStat.cpp index 11cc407e..396cbe41 100644 --- a/src/api/ExaGeoStat.cpp +++ b/src/api/ExaGeoStat.cpp @@ -29,24 +29,28 @@ using namespace exageostat::prediction; template void ExaGeoStat::ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, configurations::Configurations &aConfigurations, - dataunits::ExaGeoStatData &aData) { + std::unique_ptr> &aData) { + + LOGGER("** ExaGeoStat data generation **") // Register and create a kernel object - kernels::Kernel *pKernel = plugins::PluginRegistry>::Create(aConfigurations.GetKernelName()); + kernels::Kernel *pKernel = plugins::PluginRegistry>::Create(aConfigurations.GetKernelName(), + aConfigurations.GetTimeSlot()); // Add the data generation arguments. aConfigurations.InitializeDataGenerationArguments(); // Create a unique pointer to a DataGenerator object - //hehe unique_ptr> data_generator = DataGenerator::CreateGenerator(aConfigurations); - aData = *data_generator->CreateData(aConfigurations, aHardware, *pKernel); + aData = data_generator->CreateData(aConfigurations, aHardware, *pKernel); delete pKernel; } template T ExaGeoStat::ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - ExaGeoStatData &aData, T *apMeasurementsMatrix) { + std::unique_ptr> &aData, T *apMeasurementsMatrix) { + LOGGER("** ExaGeoStat data Modeling **") // Register and create a kernel object - kernels::Kernel *pKernel = plugins::PluginRegistry>::Create(aConfigurations.GetKernelName()); + kernels::Kernel *pKernel = plugins::PluginRegistry>::Create(aConfigurations.GetKernelName(), + aConfigurations.GetTimeSlot()); // Add the data modeling arguments. aConfigurations.InitializeDataModelingArguments(); @@ -66,6 +70,7 @@ T ExaGeoStat::ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, Con optimizing_function.set_max_objective(ExaGeoStatMLETileAPI, (void *) modeling_data); // Optimize mle using nlopt. optimizing_function.optimize(aConfigurations.GetStartingTheta(), opt_f); + aConfigurations.SetEstimatedTheta(aConfigurations.GetStartingTheta()); delete pKernel; delete modeling_data; @@ -90,11 +95,14 @@ ExaGeoStat::ExaGeoStatMLETileAPI(const std::vector &aTheta, std::vect template void ExaGeoStat::ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - ExaGeoStatData &aData, T *apMeasurementsMatrix) { + std::unique_ptr> &aData, + T *apMeasurementsMatrix) { + LOGGER("** ExaGeoStat data Prediction **") Prediction predictor; // Register and create a kernel object - kernels::Kernel *pKernel = plugins::PluginRegistry>::Create(aConfigurations.GetKernelName()); + kernels::Kernel *pKernel = plugins::PluginRegistry>::Create(aConfigurations.GetKernelName(), + aConfigurations.GetTimeSlot()); // Add the data prediction arguments. aConfigurations.InitializeDataPredictionArguments(); predictor.PredictMissingData(aHardware, aData, aConfigurations, apMeasurementsMatrix, *pKernel); diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index f7a0ada3..3371a480 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -39,7 +39,7 @@ Configurations::Configurations() { SetTimeSlot(1); SetProblemSize(0); SetDenseTileSize(0); -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA SetLowTileSize(0); #endif SetBand(0); @@ -51,7 +51,6 @@ Configurations::Configurations() { SetLowerBounds(theta); SetUpperBounds(theta); SetEstimatedTheta(theta); - SetP(1); SetSeed(0); SetLogger(false); SetUnknownObservationsNb(0); @@ -67,6 +66,7 @@ Configurations::Configurations() { SetDataPath(""); SetDistanceMetric(common::EUCLIDEAN_DISTANCE); SetAccuracy(0); + SetIsNonGaussian(false); } @@ -103,6 +103,9 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { SetPGrid(CheckNumericalValue(argument_value)); } else if (argument_name == "--Q" || argument_name == "--q") { SetQGrid(CheckNumericalValue(argument_value)); + } else if (argument_name == "--Dimension" || argument_name == "--dimension" || argument_name == "--dim" || + argument_name == "--Dim") { + SetDimension(CheckDimensionValue(argument_value)); } else if (argument_name == "--TimeSlot" || argument_name == "--timeslot" || argument_name == "--time_slot") { SetTimeSlot(CheckNumericalValue(argument_value)); @@ -118,6 +121,8 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { SetGPUsNumbers(CheckNumericalValue(argument_value)); } else if (argument_name == "--DTS" || argument_name == "--dts" || argument_name == "--Dts") { SetDenseTileSize(CheckNumericalValue(argument_value)); + } else if (argument_name == "--LTS" || argument_name == "--lts" || argument_name == "--Lts") { + SetLowTileSize(CheckNumericalValue(argument_value)); } else if (argument_name == "--maxRank" || argument_name == "--maxrank" || argument_name == "--max_rank") { SetMaxRank(CheckNumericalValue(argument_value)); } else if (argument_name == "--initial_theta" || argument_name == "--itheta" || @@ -142,19 +147,20 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { SetSeed(CheckNumericalValue(argument_value)); } else if (argument_name == "--verbose" || argument_name == "--Verbose") { ParseVerbose(argument_value); + } else if (argument_name == "--distance_metric" || argument_name == "--distanceMetric") { + ParseDistanceMetric(argument_value); } else if (argument_name == "--logpath" || argument_name == "--log_path" || argument_name == "--logPath") { SetLoggerPath(argument_value); } else { - if (!(argument_name == "--Dimension" || argument_name == "--dimension" || argument_name == "--dim" || - argument_name == "--Dim" || argument_name == "--ZmissNumber" || argument_name == "--Zmiss" || + if (!(argument_name == "--ZmissNumber" || argument_name == "--Zmiss" || argument_name == "--ZMiss" || argument_name == "--predict" || argument_name == "--Predict" || argument_name == "--iterations" || argument_name == "--Iterations" || argument_name == "--max_mle_iterations" || - argument_name == "--maxMleIterations" || argument_name == "--tolerance" || + argument_name == "--maxMleIterations" || argument_name == "--opt_iters" || + argument_name == "--tolerance" || argument_name == "--opt_tol" || argument_name == "--distanceMetric" || argument_name == "--distance_metric" || argument_name == "--log_file_name" || argument_name == "--logFileName" || argument_name == "--Band" || argument_name == "--band" || - argument_name == "--LTS" || argument_name == "--lts" || argument_name == "--Lts" || argument_name == "--DataPath" || argument_name == "--dataPath" || argument_name == "--data_path" || argument_name == "--acc" || argument_name == "--Acc")) { @@ -167,7 +173,7 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { if (argument_name == "--help") { PrintUsage(); } - if (argument_name == "--OOC") { + if (argument_name == "--OOC" || argument_name == "--ooc") { SetIsOOC(true); } else if (argument_name == "--ApproximationMode" || argument_name == "--approximationmode" || argument_name == "--approximation_mode") { @@ -177,7 +183,7 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { } else { if (!(argument_name == "--mspe" || argument_name == "--MSPE" || argument_name == "--idw" || argument_name == "--IDW" || - argument_name == "--mloe-mmom" || argument_name == "--mloe-mmom" || argument_name == "--mloe_mmom" || + argument_name == "--mloe-mmom" || argument_name == "--mloe_mmom" || argument_name == "--fisher" || argument_name == "--Fisher")) { LOGGER("!! " << argument_name << " !!") throw invalid_argument( @@ -199,19 +205,34 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { throw domain_error("You need to set the Kernel, before starting"); } + size_t found = GetKernelName().find("NonGaussian"); + // Check if the substring was found + if (found != std::string::npos) { + SetIsNonGaussian(true); + } + if (GetLogger()) { //initlog InitLog(); } this->PrintSummary(); + } void Configurations::InitializeAllTheta() { if (!mIsThetaInit) { + int parameters_number = kernels::KernelsConfigurations::GetParametersNumberKernelMap()[this->GetKernelName()]; InitTheta(GetInitialTheta(), parameters_number); SetInitialTheta(GetInitialTheta()); + + + if (this->GetIsNonGaussian()) { + GetInitialTheta()[GetInitialTheta().size() - 1] = 0.2; + GetInitialTheta()[GetInitialTheta().size() - 2] = 0.2; + } + InitTheta(GetLowerBounds(), parameters_number); SetLowerBounds(GetLowerBounds()); InitTheta(GetUpperBounds(), parameters_number); @@ -250,11 +271,8 @@ void Configurations::InitializeDataGenerationArguments() { argument_value = argument.substr(equal_sign_Idx + 1); // Check the argument name and set the corresponding value - if (argument_name == "--Dimension" || argument_name == "--dimension" || argument_name == "--dim" || - argument_name == "--Dim") { - SetDimension(CheckDimensionValue(argument_value)); - } else if (argument_name == "--DataPath" || argument_name == "--dataPath" || - argument_name == "--data_path") { + if (argument_name == "--DataPath" || argument_name == "--dataPath" || + argument_name == "--data_path") { SetDataPath(argument_value); SetIsSynthetic(false); SetIsCSV(true); @@ -262,7 +280,7 @@ void Configurations::InitializeDataGenerationArguments() { } } if (GetDimension() != DimensionST) { - if (GetTimeSlot() > 1) { + if (GetTimeSlot() != 1) { throw std::runtime_error("Time Slot can only be greater than 1 if the dimensions are set to SpaceTime."); } } else if (GetTimeSlot() < 1) { @@ -289,16 +307,13 @@ void Configurations::InitializeDataModelingArguments() { argument_value = argument.substr(equal_sign_Idx + 1); // Check the argument name and set the corresponding value - if (argument_name == "--distance_metric" || argument_name == "--distanceMetric") { - ParseDistanceMetric(argument_value); - } else if (argument_name == "--max_mle_iterations" || argument_name == "--maxMleIterations") { + if (argument_name == "--max_mle_iterations" || argument_name == "--maxMleIterations" || + argument_name == "--opt_iters") { SetMaxMleIterations(CheckNumericalValue(argument_value)); - } else if (argument_name == "--tolerance") { + } else if (argument_name == "--tolerance" || argument_name == "--opt_tol") { SetTolerance(CheckNumericalValue(argument_value)); } else if (argument_name == "--Band" || argument_name == "--band") { SetBand(CheckNumericalValue(argument_value)); - } else if (argument_name == "--LTS" || argument_name == "--lts" || argument_name == "--Lts") { - SetLowTileSize(CheckNumericalValue(argument_value)); } else if (argument_name == "--acc" || argument_name == "--Acc") { SetAccuracy(CheckNumericalValue(argument_value)); } else if (argument_name == "--log_file_name" || argument_name == "--logFileName") { @@ -316,7 +331,7 @@ void Configurations::InitializeDataModelingArguments() { } } if (GetComputation() == TILE_LOW_RANK) { -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA if (GetLowTileSize() == 0) { throw domain_error("You need to set the Low tile size, before starting"); } @@ -352,21 +367,21 @@ void Configurations::InitializeDataPredictionArguments() { argument_name = argument.substr(0, equal_sign_Idx); if (argument_name == "--mspe" || argument_name == "--MSPE") { - if (GetUnknownObservationsNb() <= 0) { + if (GetUnknownObservationsNb() <= 1) { throw domain_error( - "You need to set ZMiss number, as the number of missing values should be positive value"); + "You need to set ZMiss number, as the number of missing values should be bigger than one"); } SetIsMSPE(true); } else if (argument_name == "--idw" || argument_name == "--IDW") { - if (GetUnknownObservationsNb() <= 0) { + if (GetUnknownObservationsNb() <= 1) { throw domain_error( - "You need to set ZMiss number, as the number of missing values should be positive value"); + "You need to set ZMiss number, as the number of missing values should be bigger than one"); } SetIsIDW(true); } else if (argument_name == "--mloe-mmom" || argument_name == "--MLOE_MMOM" || argument_name == "--mloe_mmom") { - if (GetUnknownObservationsNb() <= 0) { + if (GetUnknownObservationsNb() <= 1) { throw domain_error( - "You need to set ZMiss number, as the number of missing values should be positive value"); + "You need to set ZMiss number, as the number of missing values should be bigger than one"); } SetIsMLOEMMOM(true); } else if (argument_name == "--Fisher" || argument_name == "--fisher") { @@ -390,7 +405,7 @@ void Configurations::PrintUsage() { LOGGER("--gpus=value : Used to set the number of GPUs.") LOGGER("--dts=value : Used to set the Dense Tile size.") LOGGER("--lts=value : Used to set the Low Tile size.") - LOGGER("--diag_thick=value : Used to set the Tile diagonal thickness.") + LOGGER("--band=value : Used to set the Tile diagonal thickness.") LOGGER("--Zmiss=value : Used to set number of unknown observation to be predicted.") LOGGER("--observations_file=PATH/TO/File : Used to pass the observations file path.") LOGGER("--max_rank=value : Used to the max rank value.") @@ -490,24 +505,23 @@ void Configurations::CheckKernelValue(const string &aKernel) { // Check if the kernel name exists in the availableKernels set. if (availableKernels.count(aKernel) <= 0) { throw range_error("Invalid value for Kernel. Please check manual."); - } else { - // Check if the string is already in CamelCase format - if (IsCamelCase(aKernel)) { - this->SetKernelName(aKernel); - return; - } - string str = aKernel; - // Replace underscores with spaces and split the string into words - std::replace(str.begin(), str.end(), '_', ' '); - std::istringstream iss(str); - std::string word, result; - while (iss >> word) { - // Capitalize the first letter of each word and append it to the result - word[0] = static_cast(toupper(word[0])); - result += word; - } - this->SetKernelName(result); } + // Check if the string is already in CamelCase format + if (IsCamelCase(aKernel)) { + this->SetKernelName(aKernel); + return; + } + string str = aKernel; + // Replace underscores with spaces and split the string into words + std::replace(str.begin(), str.end(), '_', ' '); + std::istringstream iss(str); + std::string word, result; + while (iss >> word) { + // Capitalize the first letter of each word and append it to the result + word[0] = static_cast(toupper(word[0])); + result += word; + } + this->SetKernelName(result); } bool Configurations::IsCamelCase(const std::string &aString) { @@ -628,45 +642,56 @@ void Configurations::PrintSummary() { Verbose temp = this->GetVerbosity(); mVerbosity = STANDARD_MODE; - if (!mIsPrinted) { - LOGGER("********************SUMMARY**********************") - if (this->GetIsSynthetic()) { - LOGGER("#Synthetic Dataset") - } else { - LOGGER("#Real Dataset") - } - LOGGER("#Number of Locations: " << this->GetProblemSize()) - LOGGER("#Threads per node: " << this->GetCoresNumber()) - LOGGER("#GPUs: " << this->GetGPUsNumbers()) - if (this->GetPrecision() == 1) { - LOGGER("#Precision: Double") - } else if (this->GetPrecision() == 0) { - LOGGER("#Precision: Single") - } else if (this->GetPrecision() == 2) { - LOGGER("#Precision: Single/Double") - } - LOGGER("#Dense Tile Size: " << this->GetDenseTileSize()) -#ifdef EXAGEOSTAT_USE_HICMA - LOGGER("#Low Tile Size: " << this->GetLowTileSize()) + LOGGER("********************SUMMARY**********************") + if (this->GetIsSynthetic()) { + LOGGER("#Synthetic Dataset") + } else { + LOGGER("#Real Dataset") + } + LOGGER("#Number of Locations: " << this->GetProblemSize()) + LOGGER("#Threads per node: " << this->GetCoresNumber()) + LOGGER("#GPUs: " << this->GetGPUsNumbers()) + if (this->GetPrecision() == 1) { + LOGGER("#Precision: Double") + } else if (this->GetPrecision() == 0) { + LOGGER("#Precision: Single") + } else if (this->GetPrecision() == 2) { + LOGGER("#Precision: Single/Double") + } + LOGGER("#Dense Tile Size: " << this->GetDenseTileSize()) +#ifdef USE_HICMA + LOGGER("#Low Tile Size: " << this->GetLowTileSize()) #endif - if (this->GetComputation() == TILE_LOW_RANK) { - LOGGER("#Computation: Tile Low Rank") - } else if (this->GetComputation() == EXACT_DENSE) { - LOGGER("#Computation: Exact") - } else if (this->GetComputation() == DIAGONAL_APPROX) { - LOGGER("#Computation: Diagonal Approx") - } - LOGGER("#Kernel: " << this->GetKernelName()) - LOGGER("#p: " << this->GetPGrid() << "\t\t #q: " << this->GetQGrid()) - LOGGER("*************************************************") - mIsPrinted = true; + if (this->GetComputation() == TILE_LOW_RANK) { + LOGGER("#Computation: Tile Low Rank") + } else if (this->GetComputation() == EXACT_DENSE) { + LOGGER("#Computation: Exact") + } else if (this->GetComputation() == DIAGONAL_APPROX) { + LOGGER("#Computation: Diagonal Approx") + } + + if (this->GetDimension() == Dimension2D) { + LOGGER("#Dimension: 2D") + } else if (this->GetDimension() == Dimension3D) { + LOGGER("#Dimension: 3D") + } else if (this->GetDimension() == DimensionST) { + LOGGER("#Dimension: ST") + } + LOGGER("#Kernel: " << this->GetKernelName()) + if (this->GetDistanceMetric() == EUCLIDEAN_DISTANCE) { + LOGGER("#Distance Metric: Euclidean distance") + } else { + LOGGER("#Distance Metric: Great Circle Distance") + } + LOGGER("#p: " << this->GetPGrid() << "\t\t #q: " << this->GetQGrid()) + if (this->GetIsOOC()) { + LOGGER("#Out Of Core (OOC) technology is enabled") } + LOGGER("*************************************************") mVerbosity = temp; } -bool Configurations::mIsPrinted = false; - int Configurations::CalculateZObsNumber() { - return this->GetProblemSize() - GetUnknownObservationsNb(); + return (this->GetProblemSize()) - this->GetUnknownObservationsNb(); } \ No newline at end of file diff --git a/src/data-generators/concrete/CSVDataGenerator.cpp b/src/data-generators/concrete/CSVDataGenerator.cpp index 820a9fab..65b27ccd 100644 --- a/src/data-generators/concrete/CSVDataGenerator.cpp +++ b/src/data-generators/concrete/CSVDataGenerator.cpp @@ -36,9 +36,11 @@ CSVDataGenerator *CSVDataGenerator::GetInstance() { } template -ExaGeoStatData *CSVDataGenerator::CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, - exageostat::kernels::Kernel &aKernel) { +unique_ptr> +CSVDataGenerator::CreateData(exageostat::configurations::Configurations &aConfigurations, + const exageostat::hardware::ExaGeoStatHardware &aHardware, + exageostat::kernels::Kernel &aKernel) { + // create vectors that will be populated with read data. vector measurements_vector; vector x_locations; @@ -46,21 +48,22 @@ ExaGeoStatData *CSVDataGenerator::CreateData(exageostat::configurations::C vector z_locations; aKernel.SetPValue(aConfigurations.GetTimeSlot()); - aConfigurations.SetP(aKernel.GetPValue()); - int p = aConfigurations.GetP(); + int p = aKernel.GetP(); //Read the data out of the CSV file. - ReadData(aConfigurations, measurements_vector, x_locations, y_locations, z_locations); + ReadData(aConfigurations, measurements_vector, x_locations, y_locations, z_locations, p); //create data object - auto *data = new ExaGeoStatData(aConfigurations.GetProblemSize() / p, aConfigurations.GetDimension()); + auto data = std::make_unique>(aConfigurations.GetProblemSize() / p, + aConfigurations.GetDimension()); //Initialize the descriptors. auto linear_algebra_solver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); linear_algebra_solver->SetContext(aHardware.GetChameleonContext()); - linear_algebra_solver->InitiateDescriptors(aConfigurations, *data->GetDescriptorData()); - linear_algebra_solver->ExaGeoStatLaSetTile(EXAGEOSTAT_UPPER_LOWER, 0, 0, data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_C).chameleon_desc); + linear_algebra_solver->InitiateDescriptors(aConfigurations, *data->GetDescriptorData(), p); + linear_algebra_solver->ExaGeoStatLaSetTile(EXAGEOSTAT_UPPER_LOWER, 0, 0, + data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_C).chameleon_desc); //populate data object with read data for (int i = 0; i < aConfigurations.GetProblemSize() / p; i++) { data->GetLocations()->GetLocationX()[i] = x_locations[i]; @@ -69,12 +72,12 @@ ExaGeoStatData *CSVDataGenerator::CreateData(exageostat::configurations::C data->GetLocations()->GetLocationZ()[i] = z_locations[i]; } } - for (int i = 0; i < aConfigurations.GetProblemSize(); i++){ + for (int i = 0; i < aConfigurations.GetProblemSize(); i++) { ((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat)[i] = measurements_vector[i]; } - results::Results::GetInstance()->SetGeneratedLocationsNumber(aConfigurations.GetProblemSize()); + results::Results::GetInstance()->SetGeneratedLocationsNumber(aConfigurations.GetProblemSize() / p); results::Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); results::Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); @@ -83,9 +86,8 @@ ExaGeoStatData *CSVDataGenerator::CreateData(exageostat::configurations::C template void -CSVDataGenerator::ReadData(Configurations &aConfigurations, - vector &aMeasurementsMatrix, vector &aXLocations, - vector &aYLocations, vector &aZLocations) { +CSVDataGenerator::ReadData(Configurations &aConfigurations, vector &aMeasurementsMatrix, vector &aXLocations, + vector &aYLocations, vector &aZLocations, const int &aP) { //Check if the user entered a valid path for the CSV file. if (aConfigurations.GetDataPath().empty()) { @@ -94,7 +96,6 @@ CSVDataGenerator::ReadData(Configurations &aConfigurations, string data_path = aConfigurations.GetDataPath(); Dimension dimension = aConfigurations.GetDimension(); - int p = aConfigurations.GetP(); ifstream file; file.open(data_path, ios::in); @@ -126,7 +127,7 @@ CSVDataGenerator::ReadData(Configurations &aConfigurations, if (dimension == Dimension2D) { aMeasurementsMatrix.push_back(stod(token)); //if p == 2, the third and fourth values of each line are saved in the Measurements Matrix. - if (p == 2) { + if (aP == 2) { if (getline(iss, token, ',')) { aMeasurementsMatrix.push_back(stod(token)); } else { @@ -134,7 +135,7 @@ CSVDataGenerator::ReadData(Configurations &aConfigurations, "The data P in the provided file isn't consistent with the Kernel's P."); } } - if (p == 3) { + if (aP == 3) { //if p == 3, the third, fourth, and fifth values of each line are saved in the Measurements Matrix. if (getline(iss, token, ',')) { aMeasurementsMatrix.push_back(stod(token)); @@ -155,7 +156,7 @@ CSVDataGenerator::ReadData(Configurations &aConfigurations, if (dimension != Dimension2D) { aMeasurementsMatrix.push_back(stod(token)); //if p == 2, the fourth and fifth values of each line are saved in the Measurements Matrix. - if (p == 2) { + if (aP == 2) { if (getline(iss, token, ',')) { aMeasurementsMatrix.push_back(stod(token)); } else { @@ -163,7 +164,7 @@ CSVDataGenerator::ReadData(Configurations &aConfigurations, "The data P in the provided file isn't consistent with the Kernel's P."); } } - if (p == 3) { + if (aP == 3) { //if p == 3, the fourth, fifth, and sixth values of each line are saved in the Measurements Matrix. if (getline(iss, token, ',')) { aMeasurementsMatrix.push_back(stod(token)); @@ -186,7 +187,7 @@ CSVDataGenerator::ReadData(Configurations &aConfigurations, } //problem size is equal to the number of the CSV lines * P / aConfigurations.GetTimeSlot(). - aConfigurations.SetProblemSize(index * p / aConfigurations.GetTimeSlot()); + aConfigurations.SetProblemSize(index * aP / aConfigurations.GetTimeSlot()); file.close(); } diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index 2f21aa6a..0ac16e63 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -33,34 +33,29 @@ SyntheticGenerator *SyntheticGenerator::GetInstance() { } template -ExaGeoStatData *SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, - exageostat::kernels::Kernel &aKernel) { - - auto *data = new ExaGeoStatData(aConfigurations.GetProblemSize(), aConfigurations.GetDimension()); +std::unique_ptr> +SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aConfigurations, + const exageostat::hardware::ExaGeoStatHardware &aHardware, + exageostat::kernels::Kernel &aKernel) { + int n = aConfigurations.GetProblemSize() * aConfigurations.GetTimeSlot(); + auto data = std::make_unique>(n, aConfigurations.GetDimension()); // Allocated new Locations object. - auto *locations = new Locations((aConfigurations.GetProblemSize() * aConfigurations.GetTimeSlot()), - aConfigurations.GetDimension()); - - // Set some kernel and arguments values. - aKernel.SetPValue(aConfigurations.GetTimeSlot()); - int N = aConfigurations.GetProblemSize(); - aConfigurations.SetProblemSize( - aConfigurations.GetProblemSize() * aKernel.GetPValue() / aConfigurations.GetTimeSlot()); + auto *locations = new Locations(n, aConfigurations.GetDimension()); - aConfigurations.SetP(aKernel.GetPValue()); int parameters_number = aKernel.GetParametersNumbers(); // Set initial theta values. Configurations::InitTheta(aConfigurations.GetInitialTheta(), parameters_number); aConfigurations.SetInitialTheta(aConfigurations.GetInitialTheta()); - GenerateLocations(N, aConfigurations.GetTimeSlot(), aConfigurations.GetDimension(), *locations); + // Generate Locations phase + GenerateLocations(n, aConfigurations.GetTimeSlot(), aConfigurations.GetDimension(), *locations); data->SetLocations(*locations); + // Generate Descriptors phase auto linear_algebra_solver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); - linear_algebra_solver->GenerateSyntheticData(aConfigurations, aHardware, *data, aKernel); + linear_algebra_solver->GenerateSyntheticData(aConfigurations, aHardware, data, aKernel); if (aConfigurations.GetLogger()) { VERBOSE("Writing generated data to the disk (Synthetic Dataset Generation Phase) .....") @@ -81,12 +76,12 @@ ExaGeoStatData *SyntheticGenerator::CreateData(exageostat::configurations: std::string path = aConfigurations.GetLoggerPath(); DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), - aConfigurations.GetProblemSize(), aConfigurations.GetP(), path, + aConfigurations.GetProblemSize(), aKernel.GetP(), path, *data->GetLocations()); #endif VERBOSE("Done.") } - results::Results::GetInstance()->SetGeneratedLocationsNumber(aConfigurations.GetProblemSize()); + results::Results::GetInstance()->SetGeneratedLocationsNumber(n); results::Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); results::Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); @@ -115,7 +110,7 @@ void SyntheticGenerator::GenerateLocations(const int &aN, const int &aTimeSlo grid[i] = i + 1; } - double range_low = -0.4, range_high = 0.4; + T range_low = -0.4, range_high = 0.4; for (auto i = 0; i < rootN && index < aN; i++) { for (auto j = 0; j < rootN && index < aN; j++) { @@ -143,20 +138,18 @@ void SyntheticGenerator::GenerateLocations(const int &aN, const int &aTimeSlo if (aDimension != DimensionST) { SortLocations(aN, aDimension, aLocations); } else { - for (auto j = 1; j < aTimeSlot; j++) { - for (auto i = 0; i < aN; i++) { - aLocations.GetLocationX()[i + j * aN] = aLocations.GetLocationX()[i]; - aLocations.GetLocationY()[i + j * aN] = aLocations.GetLocationY()[i]; - aLocations.GetLocationZ()[i + j * aN] = (double) (j + 1); - } + for (auto i = 0; i < aN; i++) { + aLocations.GetLocationX()[i] = aLocations.GetLocationX()[i]; + aLocations.GetLocationY()[i] = aLocations.GetLocationY()[i]; + aLocations.GetLocationZ()[i] = (T) (i / aTimeSlot + 1); } } } template -double SyntheticGenerator::UniformDistribution(const double &aRangeLow, const double &aRangeHigh) { - double myRand = (double) rand() / (double) (1.0 + RAND_MAX); - double range = aRangeHigh - aRangeLow; +T SyntheticGenerator::UniformDistribution(const T &aRangeLow, const T &aRangeHigh) { + T myRand = (T) rand() / (T) (1.0 + RAND_MAX); + T range = aRangeHigh - aRangeLow; return (myRand * range) + aRangeLow; } diff --git a/src/data-units/DescriptorData.cpp b/src/data-units/DescriptorData.cpp index b64f9d03..37ec11b6 100644 --- a/src/data-units/DescriptorData.cpp +++ b/src/data-units/DescriptorData.cpp @@ -29,7 +29,7 @@ DescriptorData::~DescriptorData() { const std::string &key = pair.first; if (key.find("CHAMELEON") != std::string::npos && pair.second != nullptr) { exaGeoStatDescriptor.DestroyDescriptor(common::CHAMELEON_DESCRIPTOR, pair.second); -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA // Since there are converted descriptors from Chameleon to Hicma, which have the same memory address. // So, by deleting the owner which is Chameleon, no need to delete hicma. Therefore, we remove the row of that descriptor. std::string converted_chameleon = key.substr(0, key.length() - chameleon.length()); @@ -70,7 +70,7 @@ void *DescriptorData::GetRequest() { return this->mpRequest; } -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA template HICMA_desc_t *DescriptorData::ConvertChameleonToHicma(CHAM_desc_t *apChameleonDesc) { @@ -115,7 +115,7 @@ DescriptorData::GetDescriptor(const DescriptorType &aDescriptorType, const De descriptor.chameleon_desc = (CHAM_desc_t *) this->mDictionary[GetDescriptorName(aDescriptorName) + "_CHAMELEON"]; } else { -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA if (this->mDictionary.find(GetDescriptorName(aDescriptorName) + "_HICMA") != this->mDictionary.end()) { descriptor.hicma_desc = (HICMA_desc_t *) this->mDictionary[GetDescriptorName(aDescriptorName) + "_HICMA"]; @@ -132,7 +132,7 @@ DescriptorData::GetDescriptor(const DescriptorType &aDescriptorType, const De descriptor.hicma_desc = nullptr; } #else - throw std::runtime_error("To use HiCMA descriptor you need to enable EXAGEOSTAT_USE_HICMA!"); + throw std::runtime_error("To use HiCMA descriptor you need to enable USE_HICMA!"); #endif } return descriptor; @@ -143,7 +143,7 @@ void DescriptorData::SetDescriptor(const DescriptorType &aDescriptorType, con const bool &aIsOOC, void *apMatrix, const common::FloatPoint &aFloatPoint, const int &aMB, const int &aNB, const int &aSize, const int &aLM, const int &aLN, const int &aI, const int &aJ, const int &aM, const int &aN, const int &aP, - const int &aQ) { + const int &aQ, const bool &aValidOOC) { void *descriptor; std::string type; @@ -151,17 +151,17 @@ void DescriptorData::SetDescriptor(const DescriptorType &aDescriptorType, con if (aDescriptorType == CHAMELEON_DESCRIPTOR) { descriptor = exaGeoStatDescriptor.CreateDescriptor((CHAM_desc_t *) descriptor, aDescriptorType, aIsOOC, apMatrix, aFloatPoint, aMB, aNB, aSize, aLM, aLN, aI, aJ, aM, - aN, aP, aQ); + aN, aP, aQ, aValidOOC); type = "_CHAMELEON"; } else { -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA descriptor = exaGeoStatDescriptor.CreateDescriptor((HICMA_desc_t *) descriptor, aDescriptorType, aIsOOC, apMatrix, aFloatPoint, aMB, aNB, aSize, aLM, aLN, aI, aJ, aM, - aN, aP, aQ); + aN, aP, aQ, aValidOOC); type = "_HICMA"; #else - throw std::runtime_error("To create HiCMA descriptor you need to enable EXAGEOSTAT_USE_HICMA!"); + throw std::runtime_error("To create HiCMA descriptor you need to enable USE_HICMA!"); #endif } @@ -173,10 +173,10 @@ T *DescriptorData::GetDescriptorMatrix(const common::DescriptorType &aDescrip if (aDescriptorType == common::CHAMELEON_DESCRIPTOR) { return (T *) ((CHAM_desc_t *) apDesc)->mat; } else { -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA return (T *) ((HICMA_desc_t *) apDesc)->mat; #else - throw std::runtime_error("To use Hicma descriptor you need to enable EXAGEOSTAT_USE_HICMA!"); + throw std::runtime_error("To use Hicma descriptor you need to enable USE_HICMA!"); #endif } } @@ -289,6 +289,12 @@ std::string DescriptorData::GetDescriptorName(const DescriptorName &aDescript return "DESCRIPTOR_C_TRACE"; case DESCRIPTOR_C_DIAG : return "DESCRIPTOR_C_DIAG"; + case DESCRIPTOR_SUM : + return "DESCRIPTOR_SUM"; + case DESCRIPTOR_R : + return "DESCRIPTOR_R"; + case DESCRIPTOR_R_COPY : + return "DESCRIPTOR_R_COPY"; default: throw std::invalid_argument( "The name of descriptor you provided is undefined, Please read the user manual to know the available descriptors"); diff --git a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp index 38e104d9..aefb7edc 100644 --- a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp +++ b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp @@ -14,7 +14,7 @@ #include -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA #include @@ -27,19 +27,19 @@ using namespace exageostat::dataunits::descriptor; template void * -ExaGeoStatDescriptor::CreateDescriptor(void *apDescriptor, const DescriptorType &aDescriptorType, const bool &aIsOOC, - void *apMatrix, const common::FloatPoint &aFloatPoint, const int &aMB, - const int &aNB, const int &aSize, const int &aLM, const int &aLN, - const int &aI, const int &aJ, const int &aM, const int &aN, const int &aP, - const int &aQ) { +ExaGeoStatDescriptor::CreateDescriptor(void *apDescriptor, const common::DescriptorType &aDescriptorType, + const bool &aIsOOC, void *apMatrix, const common::FloatPoint &aFloatPoint, + const int &aMB, const int &aNB, const int &aSize, const int &aLM, + const int &aLN, const int &aI, const int &aJ, const int &aM, + const int &aN, const int &aP, const int &aQ, const bool &aValidOOC) { if (aDescriptorType == CHAMELEON_DESCRIPTOR) { return ChameleonDescriptor::CreateChameleonDescriptor(apDescriptor, aIsOOC, apMatrix, aFloatPoint, aMB, aNB, - aSize, aLM, aLN, aI, aJ, aM, aN, aP, aQ); + aSize, aLM, aLN, aI, aJ, aM, aN, aP, aQ, aValidOOC); } else if (aDescriptorType == HICMA_DESCRIPTOR) { -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA return HicmaDescriptor::CreateHicmaDescriptor(apDescriptor, aIsOOC, apMatrix, aFloatPoint, aMB, aNB, aSize, - aLM, aLN, aI, aJ, aM, aN, aP, aQ); + aLM, aLN, aI, aJ, aM, aN, aP, aQ, aValidOOC); #endif } std::cerr << "Error, please select the correct descriptor type!" << std::endl; @@ -51,7 +51,7 @@ int ExaGeoStatDescriptor::DestroyDescriptor(const DescriptorType &aDescriptor if (aDescriptorType == CHAMELEON_DESCRIPTOR) { return ChameleonDescriptor::DestroyChameleonDescriptor(apDesc); } else if (aDescriptorType == HICMA_DESCRIPTOR) { -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA return HicmaDescriptor::DestroyHicmaDescriptor(apDesc); #endif } diff --git a/src/data-units/descriptor/concrete/CMakeLists.txt b/src/data-units/descriptor/concrete/CMakeLists.txt index 291edada..9b863d6d 100644 --- a/src/data-units/descriptor/concrete/CMakeLists.txt +++ b/src/data-units/descriptor/concrete/CMakeLists.txt @@ -17,7 +17,7 @@ set(SOURCES ${SOURCES} ) -if (EXAGEOSTAT_USE_HICMA) +if (USE_HICMA) list(APPEND SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/HicmaDescriptor.cpp ) diff --git a/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp b/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp index 75984603..215407bc 100644 --- a/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp +++ b/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp @@ -22,9 +22,9 @@ CHAM_desc_t *ChameleonDescriptor::CreateChameleonDescriptor(void *apDescripto const int &aNB, const int &aSize, const int &aLM, const int &aLN, const int &aI, const int &aJ, const int &aM, const int &aN, const int &aP, - const int &aQ) { + const int &aQ, const bool &aValidOOC) { auto chameleon_desc = (CHAM_desc_t *) apDescriptor; - if (aIsOOC && apMatrix == nullptr && aMB != 1 && aNB != 1) { + if (aIsOOC && apMatrix == nullptr && aMB != 1 && aNB != 1 && aValidOOC) { CHAMELEON_Desc_Create_OOC(&chameleon_desc, (cham_flttype_t) aFloatPoint, aMB, aNB, aSize, aLM, aLN, aI, aJ, aM, aN, aP, aQ); } else { diff --git a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp index 8aa5f011..dd4131f7 100644 --- a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp +++ b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp @@ -20,9 +20,9 @@ HICMA_desc_t *HicmaDescriptor::CreateHicmaDescriptor(void *apDescriptor, cons const common::FloatPoint &aFloatPoint, const int &aMB, const int &aNB, const int &aSize, const int &aLM, const int &aLN, const int &aI, const int &aJ, const int &aM, - const int &aN, const int &aP, const int &aQ) { + const int &aN, const int &aP, const int &aQ, const bool &aValidOOC) { auto hicma_desc = (HICMA_desc_t *) apDescriptor; - if (aIsOOC && apMatrix == nullptr && aMB != 1 && aNB != 1) { + if (aIsOOC && apMatrix == nullptr && aMB != 1 && aNB != 1 && aValidOOC) { HICMA_Desc_Create_OOC(&hicma_desc, (HICMA_enum) aFloatPoint, aMB, aNB, aSize, aLM, aLN, aI, aJ, aM, aN, aP, aQ); } else { HICMA_Desc_Create(&hicma_desc, apMatrix, (HICMA_enum) aFloatPoint, aMB, aNB, aSize, aLM, aLN, aI, aJ, aM, aN, diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 7419c4dc..21d489d9 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -17,12 +17,14 @@ #include #include #include +#include using namespace exageostat::hardware; ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber) { + LOGGER("** Initialise ExaGeoStat hardware **") this->mComputation = aComputation; int tag_width = 31, tag_sep = 26; // Init hardware using Chameleon @@ -34,7 +36,7 @@ ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, // Init hardware using Hicma if (aComputation == common::TILE_LOW_RANK) { -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA if (!this->mpHicmaContext) { HICMA_user_tag_size(tag_width, tag_sep); HICMA_Init(aCoreNumber, aGpuNumber); @@ -58,7 +60,7 @@ ExaGeoStatHardware::~ExaGeoStatHardware() { this->mpChameleonContext = nullptr; } if (this->mComputation == common::TILE_LOW_RANK) { -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA if (!this->mpHicmaContext) { std::cout << "No initialized context of HiCMA, Please use 'ExaGeoStatHardware::ExaGeoStatHardware(aComputation, CoreNumber, aGpuNumber);'" @@ -73,7 +75,7 @@ ExaGeoStatHardware::~ExaGeoStatHardware() { results::Results::GetInstance()->PrintEndSummary(); } -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA void *ExaGeoStatHardware::GetHicmaContext() const { if (!this->mpHicmaContext) { @@ -96,7 +98,7 @@ void *ExaGeoStatHardware::GetContext(common::Computation aComputation) const { return GetChameleonContext(); } if (aComputation == common::TILE_LOW_RANK) { -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA return GetHicmaContext(); #endif } diff --git a/src/helpers/CommunicatorMPI.cpp b/src/helpers/CommunicatorMPI.cpp index 488f386a..b63ee70e 100644 --- a/src/helpers/CommunicatorMPI.cpp +++ b/src/helpers/CommunicatorMPI.cpp @@ -25,6 +25,7 @@ CommunicatorMPI *CommunicatorMPI::GetInstance() { bool CommunicatorMPI::GetRank() const { +#ifdef USE_MPI if(!this->mIsHardwareInitialized){ return false; } @@ -34,6 +35,9 @@ bool CommunicatorMPI::GetRank() const { } return false; } +#else + return true; +#endif } void CommunicatorMPI::SetHardwareInitialization() { diff --git a/src/helpers/DistanceCalculationHelpers.cpp b/src/helpers/DistanceCalculationHelpers.cpp index 9ca5d61c..a5ff154d 100644 --- a/src/helpers/DistanceCalculationHelpers.cpp +++ b/src/helpers/DistanceCalculationHelpers.cpp @@ -18,14 +18,14 @@ using namespace exageostat::helpers; -//convert degree to radian -static double DegreeToRadian(double aDegree) { +template +T DistanceCalculationHelpers::DegreeToRadian(T aDegree) { return (aDegree * PI / 180); } template T DistanceCalculationHelpers::DistanceEarth(T &aLatitude1, T &aLongitude1, T &aLatitude2, T &aLongitude2) { - double lat1r, lon1r, lat2r, lon2r, u, v; + T lat1r, lon1r, lat2r, lon2r, u, v; lat1r = DegreeToRadian(aLatitude1); lon1r = DegreeToRadian(aLongitude1); lat2r = DegreeToRadian(aLatitude2); diff --git a/src/kernels/CMakeLists.txt b/src/kernels/CMakeLists.txt index 6b89f681..645ac8ff 100644 --- a/src/kernels/CMakeLists.txt +++ b/src/kernels/CMakeLists.txt @@ -10,29 +10,13 @@ # @author Sameh Abdulah # @date 2023-04-11 +# Automatically add all kernels in the concrete directory. +file(GLOB ALL_KERNELS ${CMAKE_CURRENT_SOURCE_DIR}/concrete/*.cpp) + # Add the Configurations.cpp file to the list of source files. set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Kernel.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternStationary.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternNonStationary.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/BivariateMaternFlexible.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/BivariateMaternParsimonious.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternNuggetsStationary.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateSpacetimeMaternStationary.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternDsigmaSquare.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternDnu.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternDbeta.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternDdsigmaSquare.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternDdsigmaSquareBeta.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternDdsigmaSquareNu.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternDdbetaBeta.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternDdbetaNu.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternDdnuNu.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/BivariateSpacetimeMaternStationary.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternNonGaussian.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateExpNonGaussian.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TrivariateMaternParsimonious.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/UnivariateMaternNonStat.cpp + ${ALL_KERNELS} ${SOURCES} PARENT_SCOPE ) \ No newline at end of file diff --git a/src/kernels/Kernel.cpp b/src/kernels/Kernel.cpp index a238accb..464282a1 100644 --- a/src/kernels/Kernel.cpp +++ b/src/kernels/Kernel.cpp @@ -59,16 +59,17 @@ T Kernel::CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aI } template -int Kernel::GetPValue() const { - return this->mP; +int Kernel::GetP() const { + return this->mCalculatedP; } template -void Kernel::SetPValue(int aP) { - // Each kernel has its own initial P value, But in case of used spacetime kernels then aP won't be equal to 1. +void Kernel::SetPValue(int aTimeSlot) { + // Each kernel has its own initial P value, But in case of used spacetime kernels then Time Slot won't be equal to 1. // In case of uni-variate spacetime P = 1 * time slot // In case of Bi-variate spacetime P = 2 * time slot - this->mP = this->mP * aP; + // P and timeslot will be constant with each created kernel, overriding the Calculated P value to handle case of calling the function multiple times. + this->mCalculatedP = this->mP * aTimeSlot; } template diff --git a/src/kernels/concrete/BivariateMaternFlexible.cpp b/src/kernels/concrete/BivariateMaternFlexible.cpp index 1e9aedad..6a12dd97 100644 --- a/src/kernels/concrete/BivariateMaternFlexible.cpp +++ b/src/kernels/concrete/BivariateMaternFlexible.cpp @@ -47,9 +47,9 @@ BivariateMaternFlexible::GenerateCovarianceMatrix(T *apMatrixA, const int &aR int i, j; int i0 = aRowOffset; int j0; - double expr1, expr2, expr12; - double con1, con2, con12, scale12, rho, nu12, sigma_square11, sigma_square22; - double scale1 = aLocalTheta[0], scale2 = aLocalTheta[1], nu1 = aLocalTheta[4], nu2 = aLocalTheta[5]; + T expr1, expr2, expr12; + T con1, con2, con12, scale12, rho, nu12, sigma_square11, sigma_square22; + T scale1 = aLocalTheta[0], scale2 = aLocalTheta[1], nu1 = aLocalTheta[4], nu2 = aLocalTheta[5]; //Remark 1 (c) of Apanasovich et al. (2012) scale12 = pow(0.5 * (pow(scale1, -2) + pow(scale2, -2)) + aLocalTheta[2] * (1 - aLocalTheta[3]), -0.5); @@ -76,7 +76,7 @@ BivariateMaternFlexible::GenerateCovarianceMatrix(T *apMatrixA, const int &aR con12 = rho * con12; i0 /= 2; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i += 2) { j0 = aColumnOffset / 2; diff --git a/src/kernels/concrete/BivariateMaternParsimonious.cpp b/src/kernels/concrete/BivariateMaternParsimonious.cpp index 123f3b92..7faffc8c 100644 --- a/src/kernels/concrete/BivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/BivariateMaternParsimonious.cpp @@ -46,8 +46,8 @@ void BivariateMaternParsimonious::GenerateCovarianceMatrix(T *apMatrixA, cons int i, j; int i0 = aRowOffset; int j0; - double expr; - double con1, con2, con12, rho, nu12; + T expr; + T con1, con2, con12, rho, nu12; con1 = pow(2, (aLocalTheta[3] - 1)) * tgamma(aLocalTheta[3]); con1 = 1.0 / con1; @@ -66,13 +66,13 @@ void BivariateMaternParsimonious::GenerateCovarianceMatrix(T *apMatrixA, cons con12 = rho * sqrt(aLocalTheta[0] * aLocalTheta[1]) * con12; i0 /= 2; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i += 2) { j0 = aColumnOffset / 2; for (j = 0; j < aColumnsNumber; j += 2) { expr = DistanceCalculationHelpers::CalculateDistance(aLocation1, aLocation2, i0, j0, aDistanceMetric, - flag) / aLocalTheta[2]; + 0) / aLocalTheta[2]; if (expr == 0) { apMatrixA[i + j * aRowsNumber] = aLocalTheta[0]; diff --git a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp index 8ef95c6e..5d26d9d7 100644 --- a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp @@ -47,9 +47,9 @@ BivariateSpacetimeMaternStationary::GenerateCovarianceMatrix(T *apMatrixA, co int i, j; int i0 = aRowOffset; int j0; - double z0, z1; - double expr, expr2, expr3, expr4; - double con1, con2, con12, rho, nu12; + T z0, z1; + T expr, expr2, expr3, expr4; + T con1, con2, con12, rho, nu12; con1 = pow(2, (aLocalTheta[3] - 1)) * tgamma(aLocalTheta[3]); con1 = 1.0 / con1; diff --git a/src/kernels/concrete/TrivariateMaternParsimonious.cpp b/src/kernels/concrete/TrivariateMaternParsimonious.cpp index a7de387b..ab0c0f5c 100644 --- a/src/kernels/concrete/TrivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/TrivariateMaternParsimonious.cpp @@ -46,8 +46,8 @@ void TrivariateMaternParsimonious::GenerateCovarianceMatrix(T *apMatrixA, con int i, j; int i0 = aRowOffset; int j0; - double expr; - double con1, con2, con3, con12, con13, con23, rho12, rho13, rho23, nu12, nu13, nu23; + T expr; + T con1, con2, con3, con12, con13, con23, rho12, rho13, rho23, nu12, nu13, nu23; con1 = pow(2, (aLocalTheta[4] - 1)) * tgamma(aLocalTheta[4]); con1 = 1.0 / con1; @@ -88,13 +88,13 @@ void TrivariateMaternParsimonious::GenerateCovarianceMatrix(T *apMatrixA, con i0 /= 3; int matrix_size = aRowsNumber * aColumnsNumber; int index; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber - 1; i += 3) { j0 = aColumnOffset / 3; for (j = 0; j < aColumnsNumber - 1; j += 3) { expr = DistanceCalculationHelpers::CalculateDistance(aLocation1, aLocation2, i0, j0, aDistanceMetric, - flag) / aLocalTheta[3]; + 0) / aLocalTheta[3]; if (expr == 0) { diff --git a/src/kernels/concrete/UnivariateExpNonGaussian.cpp b/src/kernels/concrete/UnivariateExpNonGaussian.cpp index c785cc7b..cfe224e0 100644 --- a/src/kernels/concrete/UnivariateExpNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateExpNonGaussian.cpp @@ -47,16 +47,15 @@ UnivariateExpNonGaussian::GenerateCovarianceMatrix(T *apMatrixA, const int &a int i, j; int i0 = aRowOffset; int j0; - double expr; - double sigma_square = 1; - int flag = 0; + T expr; + T sigma_square = 1; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i++) { j0 = aColumnOffset; for (j = 0; j < aColumnsNumber; j++) { expr = DistanceCalculationHelpers::CalculateDistance(aLocation1, aLocation2, i0, j0, aDistanceMetric, flag) / aLocalTheta[0]; - if (expr == 0) { apMatrixA[i + j * aRowsNumber] = sigma_square; } else { diff --git a/src/kernels/concrete/UnivariateMaternDbeta.cpp b/src/kernels/concrete/UnivariateMaternDbeta.cpp index 27137dee..afe9921a 100644 --- a/src/kernels/concrete/UnivariateMaternDbeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDbeta.cpp @@ -46,13 +46,13 @@ void UnivariateMaternDbeta::GenerateCovarianceMatrix(T *apMatrixA, const int int i, j; int i0 = aRowOffset; int j0; - double expr; - double beta_expr; - double con; - double sigma_square = aLocalTheta[0]; + T expr; + T beta_expr; + T con; + T sigma_square = aLocalTheta[0]; con = pow(2, (aLocalTheta[2] - 1)) * tgamma(aLocalTheta[2]); con = 1.0 / con; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i++) { j0 = aColumnOffset; diff --git a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp index b2d4d4e7..b084ff2d 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp @@ -47,14 +47,14 @@ UnivariateMaternDdbetaBeta::GenerateCovarianceMatrix(T *apMatrixA, const int int i, j; int i0 = aRowOffset; int j0; - double expr; - double con; - double beta_expr; - double beta_expr_prime; - double sigma_square = aLocalTheta[0]; + T expr; + T con; + T beta_expr; + T beta_expr_prime; + T sigma_square = aLocalTheta[0]; con = pow(2, (aLocalTheta[2] - 1)) * tgamma(aLocalTheta[2]); con = 1.0 / con; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i++) { j0 = aColumnOffset; diff --git a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp index 7d2b036e..3fe34441 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp @@ -47,14 +47,14 @@ UnivariateMaternDdbetaNu::GenerateCovarianceMatrix(T *apMatrixA, const int &a int i, j; int i0 = aRowOffset; int j0; - double expr; - double con; - double nu_expr; - double nu_expr_prime; - double sigma_square = aLocalTheta[0]; + T expr; + T con; + T nu_expr; + T nu_expr_prime; + T sigma_square = aLocalTheta[0]; con = pow(2, (aLocalTheta[2] - 1)) * tgamma(aLocalTheta[2]); con = 1.0 / con; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i++) { j0 = aColumnOffset; diff --git a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp index fd884838..45aa531c 100644 --- a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp @@ -47,14 +47,14 @@ UnivariateMaternDdnuNu::GenerateCovarianceMatrix(T *apMatrixA, const int &aRo int i, j; int i0 = aRowOffset; int j0; - double expr; - double con; - double nu_expr; - double nu_expr_dprime; - double sigma_square = aLocalTheta[0]; + T expr; + T con; + T nu_expr; + T nu_expr_dprime; + T sigma_square = aLocalTheta[0]; con = pow(2, (aLocalTheta[2] - 1)) * tgamma(aLocalTheta[2]); con = 1.0 / con; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i++) { j0 = aColumnOffset; diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp index af410d57..3e1a0468 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp @@ -44,8 +44,8 @@ void UnivariateMaternDdsigmaSquare::GenerateCovarianceMatrix(T *apMatrixA, co int i, j; int i0 = aRowOffset; int j0; - double expr; - double con; + T expr; + T con; con = pow(2, (aLocalTheta[2] - 1)) * tgamma(aLocalTheta[2]); con = 1.0 / con; diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp index 579d1dcc..c3e43818 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp @@ -46,12 +46,12 @@ void UnivariateMaternDdsigmaSquareBeta::GenerateCovarianceMatrix(T *apMatrixA int i, j; int i0 = aRowOffset; int j0; - double expr; - double con; - double beta_expr; + T expr; + T con; + T beta_expr; con = pow(2, (aLocalTheta[2] - 1)) * tgamma(aLocalTheta[2]); con = 1.0 / con; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i++) { j0 = aColumnOffset; diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp index 7563c676..4861116b 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp @@ -46,9 +46,9 @@ void UnivariateMaternDdsigmaSquareNu::GenerateCovarianceMatrix(T *apMatrixA, int i, j; int i0 = aRowOffset; int j0; - double expr; - double nu_expr; - int flag = 0; + T expr; + T nu_expr; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i++) { j0 = aColumnOffset; diff --git a/src/kernels/concrete/UnivariateMaternDnu.cpp b/src/kernels/concrete/UnivariateMaternDnu.cpp index 8830f0f8..c34b6e13 100644 --- a/src/kernels/concrete/UnivariateMaternDnu.cpp +++ b/src/kernels/concrete/UnivariateMaternDnu.cpp @@ -46,10 +46,10 @@ void UnivariateMaternDnu::GenerateCovarianceMatrix(T *apMatrixA, const int &a int i, j; int i0 = aRowOffset; int j0; - double expr; - double nu_expr; - double sigma_square = aLocalTheta[0]; - int flag = 0; + T expr; + T nu_expr; + T sigma_square = aLocalTheta[0]; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i++) { j0 = aColumnOffset; diff --git a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp index f48bd9b0..d4f86d09 100644 --- a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp @@ -47,12 +47,12 @@ void UnivariateMaternDsigmaSquare::GenerateCovarianceMatrix(T *apMatrixA, con int i, j; int i0 = aRowOffset; int j0; - double expr; - double con; + T expr; + T con; con = pow(2, (aLocalTheta[2] - 1)) * tgamma(aLocalTheta[2]); con = 1.0 / con; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i++) { j0 = aColumnOffset; for (j = 0; j < aColumnsNumber; j++) { diff --git a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp index 2a10db0f..0cd0ab0a 100644 --- a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp @@ -46,14 +46,14 @@ void UnivariateMaternNonGaussian::GenerateCovarianceMatrix(T *apMatrixA, cons int i, j; int i0 = aRowOffset; int j0; - double expr; - double con; - double sigma_square = 1; + T expr; + T con; + T sigma_square = 1; con = pow(2, (aLocalTheta[1] - 1)) * tgamma(aLocalTheta[1]); con = 1.0 / con; con = sigma_square * con; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; for (i = 0; i < aRowsNumber; i++) { j0 = aColumnOffset; diff --git a/src/kernels/concrete/UnivariateMaternNonStat.cpp b/src/kernels/concrete/UnivariateMaternNonStat.cpp deleted file mode 100644 index b2ee916f..00000000 --- a/src/kernels/concrete/UnivariateMaternNonStat.cpp +++ /dev/null @@ -1,145 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file UnivariateMaternNonStat.cpp - * @brief Implementation of the UnivariateMaternNonStat kernel. - * @version 1.0.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @date 2023-04-14 -**/ - -#include - -using namespace exageostat::kernels; -using namespace exageostat::dataunits; - -template -UnivariateMaternNonStat::UnivariateMaternNonStat() { - this->mP = 1; - this->mParametersNumber = 8; -} - -template -Kernel *UnivariateMaternNonStat::Create() { - KernelsConfigurations::GetParametersNumberKernelMap()["UnivariateMaternNonStat"] = 8; - return new UnivariateMaternNonStat(); -} - -namespace exageostat::kernels { - template bool UnivariateMaternNonStat::plugin_name = plugins::PluginRegistry>::Add( - "UnivariateMaternNonStat", UnivariateMaternNonStat::Create); -} - -template -void -UnivariateMaternNonStat::GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, - const int &aRowOffset, const int &aColumnOffset, - Locations &aLocation1, Locations &aLocation2, - Locations &aLocation3, T *aLocalTheta, - const int &aDistanceMetric) { - - double l1x, l1y, l2x, l2y; - double a, b, d, e, f, g, h, ti; - a = aLocalTheta[0]; - b = aLocalTheta[1]; - d = aLocalTheta[2]; - e = aLocalTheta[3]; - f = aLocalTheta[4]; - g = aLocalTheta[5]; - h = aLocalTheta[6]; - ti = aLocalTheta[7]; - - double nu_arr_1[aRowsNumber]; - double sigma_arr_1[aRowsNumber]; - double lambda_arr_1[aRowsNumber]; - double nu_arr_2[aColumnsNumber]; - double sigma_arr_2[aColumnsNumber]; - double lambda_arr_2[aColumnsNumber]; - double term1; - double term2; - double neuij; - double Qij; - double prod1; - double term3; - - for (int i = 0; i < aRowsNumber; i++) { - l1x = aLocation1.GetLocationX()[i + aRowOffset]; - l1y = aLocation1.GetLocationY()[i + aRowOffset]; - nu_arr_1[i] = Neu(l1x, l1y, g, h, ti); - sigma_arr_1[i] = Sigma(l1x, l1y, d, e, f); - lambda_arr_1[i] = Lambda(l1x, l1y, a, b); - } - - for (int j = 0; j < aColumnsNumber; j++) { - l2x = aLocation2.GetLocationX()[j + aColumnOffset]; - l2y = aLocation2.GetLocationY()[j + aColumnOffset]; - - nu_arr_2[j] = Neu(l2x, l2y, g, h, ti); - sigma_arr_2[j] = Sigma(l2x, l2y, d, e, f); - lambda_arr_2[j] = Lambda(l2x, l2y, a, b); - } - - for (int i = 0; i < aRowsNumber; i++) { - l1x = aLocation1.GetLocationX()[i + aRowOffset]; - l1y = aLocation1.GetLocationY()[i + aRowOffset]; - - for (int j = 0; j < aColumnsNumber; j++) { - l2x = aLocation2.GetLocationX()[j + aColumnOffset]; - l2y = aLocation2.GetLocationY()[j + aColumnOffset]; - term1 = (sigma_arr_1[i]) * (sigma_arr_2[j]) * sqrt(lambda_arr_1[i]) * sqrt(lambda_arr_2[j]); - term2 = 2 / ((lambda_arr_1[i]) + (lambda_arr_2[j])); - neuij = ((nu_arr_1[i]) + (nu_arr_2[j])) / 2; - Qij = CalculateMahalanobisDistanceSquared(l1x, l1y, l2x, l2y, term2, 0, 0, term2); - prod1 = 2 * sqrt(neuij * Qij); - term3 = MaternUtil(1, neuij, prod1); - apMatrixA[i + j * aRowsNumber] = term1 * term2 * term3; - } - } -} - -template -double UnivariateMaternNonStat::Neu(double x, double y, double g, double h, double ti) { - return (g * pow(POW_e, h * (x + y)) + ti); -} - -template -double UnivariateMaternNonStat::Sigma(double x, double y, double d, double e, double f) { - return (d * pow(POW_e, e * (x + y)) + f); -} - -template -double UnivariateMaternNonStat::Lambda(double x, double y, double a, double b) { - return (a * pow(POW_e, sin(b * x) + sin(b * y))); -} - -template -double -UnivariateMaternNonStat::CalculateMahalanobisDistanceSquared(double x1, double y1, double x2, double y2, double a11, - double a12, double a21, double a22) { - - double diffx = x1 - x2; - double diffy = y1 - y2; - double el1 = a11 * diffx + a21 * diffy; - double el2 = a12 * diffx + a22 * diffy; - double ans = el1 * diffx + el2 * diffy; - - return ans; -} - -template -double UnivariateMaternNonStat::MaternUtil(double range, double smoothness, double distance) { - double con; - con = pow(2, (smoothness - 1)) * tgamma(smoothness); - con = 1.0 / con; - - if (distance == 0) { - return 1; - } else { - // Matern Function - return con * pow(distance / range, smoothness) * gsl_sf_bessel_Knu(smoothness, distance / range); - } -} \ No newline at end of file diff --git a/src/kernels/concrete/UnivariateMaternNonStationary.cpp b/src/kernels/concrete/UnivariateMaternNonStationary.cpp deleted file mode 100644 index 72b47001..00000000 --- a/src/kernels/concrete/UnivariateMaternNonStationary.cpp +++ /dev/null @@ -1,115 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file UnivariateMaternNonStationary.cpp - * @brief Implementation of the UnivariateMaternNonStationary kernel. - * @version 1.0.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @date 2023-04-13 -**/ - -#include -#include - -using namespace exageostat::kernels; -using namespace exageostat::dataunits; -using namespace exageostat::helpers; - -template -UnivariateMaternNonStationary::UnivariateMaternNonStationary() { - this->mP = 1; - this->mParametersNumber = 9; -} - -template -Kernel *UnivariateMaternNonStationary::Create() { - KernelsConfigurations::GetParametersNumberKernelMap()["UnivariateMaternNonStationary"] = 9; - return new UnivariateMaternNonStationary(); -} - -namespace exageostat::kernels { - template bool UnivariateMaternNonStationary::plugin_name = plugins::PluginRegistry>::Add( - "UnivariateMaternNonStationary", UnivariateMaternNonStationary::Create); -} - -template -void UnivariateMaternNonStationary::GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, - const int &aColumnsNumber, const int &aRowOffset, - const int &aColumnOffset, Locations &aLocation1, - Locations &aLocation2, Locations &aLocation3, - T *aLocalTheta, const int &aDistanceMetric) { - - double location1X, location1Y, location2X, location2Y, location3X, location3Y; - double theta_0i, theta_0j, theta_1i, theta_1j, theta_2i, theta_2j; - double dx, dy; - double dist; - double con, sigma_square, beta, nu; - int i, j; - - aLocation3 = aLocation1; - double x_max = aLocation1->GetLocationX()[0]; - double x_min = aLocation1->GetLocationX()[0]; - double y_max = aLocation1->GetLocationY()[0]; - double y_min = aLocation1->GetLocationY()[0]; - for (i = 1; i < 9; i++) { - if (x_max < aLocation1->GetLocationX()[i]) - x_max = aLocation1->GetLocationX()[i]; - if (x_min > aLocation1->GetLocationX()[i]) - x_min = aLocation1->GetLocationX()[i]; - if (y_max < aLocation1->GetLocationY()[i]) - y_max = aLocation1->GetLocationY()[i]; - if (y_max > aLocation1->GetLocationY()[i]) - y_max = aLocation1->GetLocationY()[i]; - } - - aLocation3->GetLocationX()[0] = x_min + (x_max - x_min) / 2; - aLocation3->GetLocationY()[0] = y_min + (y_max - y_min) / 2; - LOGGER(" The central point is ( %f, %f)\n", aLocation3->GetLocationX()[0], aLocation3->GetLocationY()[0]); - - // Compute the covariance matrix elements - for (j = 0; j < aColumnsNumber; j++) { - location1X = aLocation1->GetLocationX()[aColumnOffset + j]; - location1Y = aLocation1->GetLocationY()[aColumnOffset + j]; - location3X = aLocation3->GetLocationX()[j]; - location3Y = aLocation3->GetLocationY()[j]; - dx = abs(location1X - location3X); - dy = abs(location1Y - location3Y); - - theta_0i = aLocalTheta[0] + (aLocalTheta[1] * dx) + (aLocalTheta[2] * dy); - theta_1i = aLocalTheta[3] + (aLocalTheta[4] * dx) + (aLocalTheta[5] * dy); - theta_2i = aLocalTheta[6] + (aLocalTheta[7] * dx) + (aLocalTheta[8] * dy); - - for (i = 0; i < aRowsNumber; i++) { - location2X = aLocation2->GetLocationX()[aRowOffset + i]; - location2Y = aLocation2->GetLocationY()[aRowOffset + i]; - location3X = aLocation3->GetLocationX()[i]; - location3Y = aLocation3->GetLocationY()[i]; - dx = abs(location2X - location3X); - dy = abs(location2Y - location3Y); - - theta_0j = aLocalTheta[0] + (aLocalTheta[1] * dx) + (aLocalTheta[2] * dy); - theta_1j = aLocalTheta[3] + (aLocalTheta[4] * dx) + (aLocalTheta[5] * dy); - theta_2j = aLocalTheta[6] + (aLocalTheta[7] * dx) + (aLocalTheta[8] * dy); - - // Compute the Matérn parameters and distance metric - sigma_square = (theta_0i + theta_0j) / 2; - beta = (theta_1i + theta_1j) / 2; - nu = (theta_2i + theta_2j) / 2; - con = pow(2, (nu - 1)) / tgamma(nu); - - con = 1.0 / con; - con = sigma_square * con; - int flag = 0; - - //MLE calculation - dist = DistanceCalculationHelpers::CalculateDistance(aLocation1, aLocation2, i, j, aDistanceMetric, - flag) / beta; - *(apMatrixA + i + j * aRowsNumber) = (dist == 0.0) ? sigma_square : con * pow(dist, nu) * - gsl_sf_bessel_Knu(nu, dist); - } - } -} diff --git a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp index d2729283..0ab266c4 100644 --- a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp @@ -46,14 +46,14 @@ void UnivariateMaternNuggetsStationary::GenerateCovarianceMatrix(T *apMatrixA int i, j; int i0 = aRowOffset; int j0; - double expr; - double con; - double sigma_square = aLocalTheta[0]; + T expr; + T con; + T sigma_square = aLocalTheta[0]; con = pow(2, (aLocalTheta[2] - 1)) * tgamma(aLocalTheta[2]); con = 1.0 / con; con = sigma_square * con; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; if (aLocation1.GetLocationZ() == nullptr || aLocation2.GetLocationZ() == nullptr) { for (i = 0; i < aRowsNumber; i++) { diff --git a/src/kernels/concrete/UnivariateMaternStationary.cpp b/src/kernels/concrete/UnivariateMaternStationary.cpp index 4fe9c1aa..0bdd8d06 100644 --- a/src/kernels/concrete/UnivariateMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternStationary.cpp @@ -48,7 +48,7 @@ UnivariateMaternStationary::GenerateCovarianceMatrix(T *apMatrixA, const int const T nu = aLocalTheta[2]; const T inv_con = sigma_square * (1.0 / (pow(2, (nu - 1)) * tgamma((nu)))); int i0 = aRowOffset; - int flag = 0; + int flag = aLocation1.GetLocationZ() == nullptr ? 0 : 1; int j0; int i, j; T dist; diff --git a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp index 1eb5d63f..1ccb8add 100644 --- a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp @@ -51,10 +51,10 @@ UnivariateSpacetimeMaternStationary::GenerateCovarianceMatrix(T *apMatrixA, c int i, j; int i0 = aRowOffset; int j0; - double z0, z1; - double expr, expr2, expr3, expr4; - double con; - double sigma_square = aLocalTheta[0]; + T z0, z1; + T expr, expr2, expr3, expr4; + T con; + T sigma_square = aLocalTheta[0]; con = pow(2, (aLocalTheta[2] - 1)) * tgamma(aLocalTheta[2]); con = 1.0 / con; diff --git a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp index ec759d52..ddfd312e 100644 --- a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp @@ -19,7 +19,7 @@ #include #include -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA #include @@ -40,11 +40,11 @@ std::unique_ptr> LinearAlgebraFactory::CreateLinearAl // HiCMA Used else if (aComputation == TILE_LOW_RANK) { -#ifdef EXAGEOSTAT_USE_HICMA +#ifdef USE_HICMA return std::make_unique>(); #else throw std::runtime_error( - "Tile low rank generation isn't supported without enabling HiCMA. Use -DEXAGEOSTAT_USE_HICMA=ON"); + "Tile low rank generation isn't supported without enabling HiCMA. Use -DUSE_HICMA=ON"); #endif } else if (aComputation == DIAGONAL_APPROX) { return std::make_unique>(); diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index 5cf36748..6d6dc04e 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -15,6 +15,7 @@ #ifdef USE_MPI #include #endif + #include #include @@ -30,7 +31,7 @@ using namespace exageostat::hardware; // Define a method to set up the Chameleon descriptors template void LinearAlgebraMethods::InitiateDescriptors(Configurations &aConfigurations, DescriptorData &aDescriptorData, - T *apMeasurementsMatrix) { + const int &aP, T *apMeasurementsMatrix) { // Check for initialize the Chameleon context. if (!this->mpContext) { @@ -39,7 +40,7 @@ void LinearAlgebraMethods::InitiateDescriptors(Configurations &aConfiguration } // Get the problem size and other configuration parameters - int n = aConfigurations.GetProblemSize(); + int full_problem_size = aConfigurations.GetProblemSize() * aP; int dts = aConfigurations.GetDenseTileSize(); int p_grid = aConfigurations.GetPGrid(); int q_grid = aConfigurations.GetQGrid(); @@ -65,36 +66,48 @@ void LinearAlgebraMethods::InitiateDescriptors(Configurations &aConfiguration } aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C, is_OOC, nullptr, float_point, dts, dts, - dts * dts, n, n, 0, 0, n, n, p_grid, q_grid); + dts * dts, full_problem_size, full_problem_size, 0, 0, full_problem_size, + full_problem_size, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z, is_OOC, apMeasurementsMatrix, float_point, - dts, dts, dts * dts, n, 1, 0, 0, n, 1, p_grid, q_grid); + dts, dts, dts * dts, full_problem_size, 1, 0, 0, full_problem_size, 1, p_grid, + q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_COPY, is_OOC, nullptr, float_point, dts, - dts, dts * dts, n, 1, 0, 0, n, 1, p_grid, q_grid); + dts, dts * dts, full_problem_size, 1, 0, 0, full_problem_size, 1, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_DETERMINANT, is_OOC, nullptr, float_point, - dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); + dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid, false); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT, is_OOC, nullptr, float_point, dts, - dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); + dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid, false); if (float_point == EXAGEOSTAT_REAL_DOUBLE) { auto *CHAM_descC = aDescriptorData.GetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C11, is_OOC, nullptr, float_point, dts, - dts, dts * dts, n, n, 0, 0, CHAM_descC->m / 2, CHAM_descC->n / 2, p_grid, q_grid); + dts, dts * dts, full_problem_size, full_problem_size, 0, 0, CHAM_descC->m / 2, + CHAM_descC->n / 2, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C12, is_OOC, nullptr, float_point, dts, - dts, dts * dts, n, n, CHAM_descC->m / 2, 0, CHAM_descC->m / 2, CHAM_descC->n / 2, + dts, dts * dts, full_problem_size, full_problem_size, CHAM_descC->m / 2, 0, + CHAM_descC->m / 2, CHAM_descC->n / 2, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C22, is_OOC, nullptr, float_point, dts, - dts, dts * dts, n, n, CHAM_descC->m / 2, CHAM_descC->n / 2, CHAM_descC->m / 2, + dts, dts * dts, full_problem_size, full_problem_size, CHAM_descC->m / 2, + CHAM_descC->n / 2, CHAM_descC->m / 2, CHAM_descC->n / 2, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_1, is_OOC, nullptr, float_point, dts, - dts, dts * dts, n / 2, 1, 0, 0, n / 2, 1, p_grid, q_grid); + dts, dts * dts, full_problem_size / 2, 1, 0, 0, full_problem_size / 2, 1, p_grid, + q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_2, is_OOC, nullptr, float_point, dts, - dts, dts * dts, n / 2, 1, 0, 0, n / 2, 1, p_grid, q_grid); + dts, dts * dts, full_problem_size / 2, 1, 0, 0, full_problem_size / 2, 1, p_grid, + q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT_1, is_OOC, nullptr, float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT_2, is_OOC, nullptr, float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); } + if (aConfigurations.GetIsNonGaussian()) { + aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_SUM, is_OOC, nullptr, float_point, + dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); + } + //stop gsl error handler gsl_set_error_handler_off(); aDescriptorData.SetIsDescriptorInitiated(true); @@ -111,7 +124,7 @@ void LinearAlgebraMethods::InitiateFisherDescriptors(configurations::Configur } // Get the problem size and other configuration parameters - int n = aConfigurations.GetProblemSize(); + int full_problem_size = aConfigurations.GetProblemSize(); int num_params = kernels::KernelsConfigurations::GetParametersNumberKernelMap()[aConfigurations.GetKernelName()]; int dts = aConfigurations.GetDenseTileSize(); int p_grid = aConfigurations.GetPGrid(); @@ -139,31 +152,36 @@ void LinearAlgebraMethods::InitiateFisherDescriptors(configurations::Configur if (!aDescriptorData.GetIsDescriptorInitiated()) { aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C, is_OOC, nullptr, float_point, dts, - dts, dts * dts, n, n, 0, 0, n, n, p_grid, q_grid); + dts, dts * dts, full_problem_size, full_problem_size, 0, 0, full_problem_size, + full_problem_size, p_grid, q_grid); } aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, common::DESCRIPTOR_A, is_OOC, nullptr, float_point, dts, dts, dts * dts, num_params, num_params, 0, 0, num_params, num_params, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, common::DESCRIPTOR_CJ, is_OOC, nullptr, float_point, - dts, dts, dts * dts, n, n, 0, 0, n, n, p_grid, q_grid); + dts, dts, dts * dts, full_problem_size, full_problem_size, 0, 0, full_problem_size, + full_problem_size, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, common::DESCRIPTOR_CK, is_OOC, nullptr, float_point, - dts, dts, dts * dts, n, n, 0, 0, n, n, p_grid, q_grid); + dts, dts, dts * dts, full_problem_size, full_problem_size, 0, 0, full_problem_size, + full_problem_size, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, common::DESCRIPTOR_RESULTS, is_OOC, nullptr, - float_point, dts, dts, dts * dts, n, n, 0, 0, n, n, p_grid, q_grid); + float_point, dts, dts, dts * dts, full_problem_size, full_problem_size, 0, 0, + full_problem_size, full_problem_size, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, common::DESCRIPTOR_C_TRACE, is_OOC, nullptr, float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); aDescriptorData.SetDescriptor(common::CHAMELEON_DESCRIPTOR, common::DESCRIPTOR_C_DIAG, is_OOC, nullptr, float_point, - dts, dts, dts * dts, n, 1, 0, 0, n, 1, p_grid, q_grid); + dts, dts, dts * dts, full_problem_size, 1, 0, 0, full_problem_size, 1, p_grid, + q_grid); } template void LinearAlgebraMethods::InitiatePredictionDescriptors( - Configurations &aConfigurations, ExaGeoStatData &aData) { + Configurations &aConfigurations, std::unique_ptr> &aData, const int &aP) { if (!this->mpContext) { throw std::runtime_error( @@ -186,44 +204,57 @@ void LinearAlgebraMethods::InitiatePredictionDescriptors( } else { throw runtime_error("Unsupported for now!"); } - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_OBSERVATIONS, is_OOC, nullptr, - float_point, dts, dts, dts * dts, n_z_obs, 1, 0, 0, n_z_obs, 1, p_grid, - q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_OBSERVATIONS, is_OOC, nullptr, + float_point, dts, dts, dts * dts, n_z_obs, 1, 0, 0, n_z_obs, 1, p_grid, + q_grid); if (aConfigurations.GetIsMSPE()) { - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_Actual, is_OOC, nullptr, - float_point, dts, dts, dts * dts, z_miss_number, 1, 0, 0, - z_miss_number, 1, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE, is_OOC, nullptr, - float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE_1, is_OOC, nullptr, - float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE_2, is_OOC, nullptr, - float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); - } - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_MISS, is_OOC, nullptr, - float_point, dts, dts, dts * dts, z_miss_number, 1, 0, 0, z_miss_number, 1, - p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_Actual, is_OOC, nullptr, + float_point, dts, dts, dts * dts, z_miss_number, 1, 0, 0, + z_miss_number, 1, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE, is_OOC, nullptr, + float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE_1, is_OOC, nullptr, + float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE_2, is_OOC, nullptr, + float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); + } + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_MISS, is_OOC, nullptr, + float_point, dts, dts, dts * dts, z_miss_number, 1, 0, 0, z_miss_number, + 1, p_grid, q_grid); descriptor::ExaGeoStatDescriptor exaGeoStatDescriptor; - auto *CHAM_descC12 = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C12).chameleon_desc; + auto *CHAM_descC12 = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C12).chameleon_desc; if (CHAM_descC12) { exaGeoStatDescriptor.DestroyDescriptor(CHAMELEON_DESCRIPTOR, CHAM_descC12); } - auto *CHAM_descC22 = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C22).chameleon_desc; + auto *CHAM_descC22 = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C22).chameleon_desc; if (CHAM_descC22) { exaGeoStatDescriptor.DestroyDescriptor(CHAMELEON_DESCRIPTOR, CHAM_descC22); } - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C12, is_OOC, nullptr, float_point, - dts, dts, dts * dts, z_miss_number, n_z_obs, 0, 0, z_miss_number, n_z_obs, - p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C22, is_OOC, nullptr, float_point, - dts, dts, dts * dts, n_z_obs, n_z_obs, 0, 0, n_z_obs, n_z_obs, p_grid, - q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C22, is_OOC, nullptr, + float_point, dts, dts, dts * dts, n_z_obs, n_z_obs, 0, 0, n_z_obs, + n_z_obs, p_grid, q_grid); + if (aConfigurations.GetIsNonGaussian()) { + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_R, is_OOC, nullptr, + float_point, dts, dts, dts * dts, n_z_obs, z_miss_number, 0, 0, + n_z_obs, z_miss_number, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_R_COPY, is_OOC, nullptr, + float_point, dts, dts, dts * dts, n_z_obs, z_miss_number, 0, 0, + n_z_obs, z_miss_number, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C12, is_OOC, nullptr, + float_point, dts, dts, dts * dts, z_miss_number, z_miss_number, 0, 0, + z_miss_number, z_miss_number, p_grid, q_grid); + } else { + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_C12, is_OOC, nullptr, + float_point, dts, dts, dts * dts, z_miss_number, n_z_obs, 0, 0, + z_miss_number, n_z_obs, p_grid, q_grid); + } } - template -void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfigurations, ExaGeoStatData &aData) { +void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfigurations, + std::unique_ptr> &aData, + const int &aP) { if (!this->mpContext) { throw std::runtime_error( @@ -235,7 +266,6 @@ void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfi int p_grid = aConfigurations.GetPGrid(); int q_grid = aConfigurations.GetQGrid(); bool is_OOC = aConfigurations.GetIsOOC(); - int P = aConfigurations.GetP(); FloatPoint float_point; if (sizeof(T) == SIZE_OF_FLOAT) { @@ -245,39 +275,42 @@ void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfi } else { throw runtime_error("Unsupported for now!"); } - - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_k_T, is_OOC, nullptr, float_point, - dts, dts, dts * dts, P * n_z_obs, P, 0, 0, P * n_z_obs, P, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_k_A, is_OOC, nullptr, float_point, - dts, dts, dts * dts, P * n_z_obs, P, 0, 0, P * n_z_obs, P, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_k_A_TMP, is_OOC, nullptr, - float_point, dts, dts, dts * dts, P * n_z_obs, P, 0, 0, P * n_z_obs, P, - p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_k_T_TMP, is_OOC, nullptr, - float_point, dts, dts, dts * dts, P * n_z_obs, P, 0, 0, P * n_z_obs, P, - p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_EXPR_1, is_OOC, nullptr, - float_point, dts, dts, dts * dts, P, P, 0, 0, P, P, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_EXPR_2, is_OOC, nullptr, - float_point, dts, dts, dts * dts, P, P, 0, 0, P, P, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_EXPR_3, is_OOC, nullptr, - float_point, dts, dts, dts * dts, P, P, 0, 0, P, P, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_EXPR_4, is_OOC, nullptr, - float_point, dts, dts, dts * dts, P, P, 0, 0, P, P, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_MLOE, is_OOC, nullptr, - float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_MMOM, is_OOC, nullptr, - float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_TRUTH_ALPHA, is_OOC, nullptr, - float_point, dts, dts, dts * dts, P, P, 0, 0, P, P, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_TIMATED_ALPHA, is_OOC, nullptr, - float_point, dts, dts, dts * dts, P, P, 0, 0, P, P, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_K_T, is_OOC, nullptr, float_point, - dts, dts, dts * dts, P * n_z_obs, P * n_z_obs, 0, 0, P * n_z_obs, - P * n_z_obs, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_K_A, is_OOC, nullptr, float_point, - dts, dts, dts * dts, P * n_z_obs, P * n_z_obs, 0, 0, P * n_z_obs, - P * n_z_obs, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_k_T, is_OOC, nullptr, + float_point, + dts, dts, dts * dts, n_z_obs, aP, 0, 0, n_z_obs, aP, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_k_A, is_OOC, nullptr, + float_point, + dts, dts, dts * dts, n_z_obs, aP, 0, 0, n_z_obs, aP, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_k_A_TMP, is_OOC, nullptr, + float_point, dts, dts, dts * dts, n_z_obs, aP, 0, 0, n_z_obs, aP, + p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_k_T_TMP, is_OOC, nullptr, + float_point, dts, dts, dts * dts, n_z_obs, aP, 0, 0, n_z_obs, aP, + p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_EXPR_1, is_OOC, nullptr, + float_point, dts, dts, dts * dts, aP, aP, 0, 0, aP, aP, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_EXPR_2, is_OOC, nullptr, + float_point, dts, dts, dts * dts, aP, aP, 0, 0, aP, aP, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_EXPR_3, is_OOC, nullptr, + float_point, dts, dts, dts * dts, aP, aP, 0, 0, aP, aP, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_EXPR_4, is_OOC, nullptr, + float_point, dts, dts, dts * dts, aP, aP, 0, 0, aP, aP, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_MLOE, is_OOC, nullptr, + float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_MMOM, is_OOC, nullptr, + float_point, dts, dts, dts * dts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_TRUTH_ALPHA, is_OOC, nullptr, + float_point, dts, dts, dts * dts, aP, aP, 0, 0, aP, aP, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_TIMATED_ALPHA, is_OOC, nullptr, + float_point, dts, dts, dts * dts, aP, aP, 0, 0, aP, aP, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_K_T, is_OOC, nullptr, + float_point, + dts, dts, dts * dts, n_z_obs, n_z_obs, 0, 0, n_z_obs, + n_z_obs, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::CHAMELEON_DESCRIPTOR, DESCRIPTOR_K_A, is_OOC, nullptr, + float_point, + dts, dts, dts * dts, n_z_obs, n_z_obs, 0, 0, n_z_obs, + n_z_obs, p_grid, q_grid); //stop gsl error handler gsl_set_error_handler_off(); } @@ -285,18 +318,19 @@ void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfi template void LinearAlgebraMethods::GenerateSyntheticData(configurations::Configurations &aConfigurations, const hardware::ExaGeoStatHardware &aHardware, - dataunits::ExaGeoStatData &aData, + std::unique_ptr> &aData, const kernels::Kernel &aKernel) { this->mpContext = aHardware.GetChameleonContext(); - this->InitiateDescriptors(aConfigurations, *aData.GetDescriptorData()); - auto median_locations = Locations(1, aData.GetLocations()->GetDimension()); - this->GenerateObservationsVector(aConfigurations, aData, aData.GetLocations(), aData.GetLocations(), - &median_locations, 0, aKernel); + this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(), aKernel.GetP()); + auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); + this->GenerateObservationsVector(aConfigurations, aData, aData->GetLocations(), aData->GetLocations(), + &median_locations, aConfigurations.GetDistanceMetric(), aKernel); } template -void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfigurations, ExaGeoStatData &aData, +void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfigurations, + std::unique_ptr> &aData, Locations *apLocation1, Locations *apLocation2, Locations *apLocation3, const int &aDistanceMetric, const kernels::Kernel &aKernel) { @@ -306,8 +340,8 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig throw std::runtime_error( "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); } - - const int n = aConfigurations.GetProblemSize(); + const int P = aKernel.GetP(); + const int full_problem_size = aConfigurations.GetProblemSize() * P; int seed = aConfigurations.GetSeed(); int initial_seed[4] = {seed, seed, seed, 1}; T time_facto = 0.0, time_trmm = 0.0, matrix_gen_time = 0.0, flops = 0; @@ -315,17 +349,17 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig // Create a Chameleon sequence, if not initialized before through the same descriptors RUNTIME_request_t request_array[2] = {RUNTIME_REQUEST_INITIALIZER, RUNTIME_REQUEST_INITIALIZER}; RUNTIME_sequence_t *sequence; - if (!aData.GetDescriptorData()->GetSequence()) { + if (!aData->GetDescriptorData()->GetSequence()) { ExaGeoStatCreateSequence(&sequence); - aData.GetDescriptorData()->SetSequence(sequence); - aData.GetDescriptorData()->SetRequest(request_array); + aData->GetDescriptorData()->SetSequence(sequence); + aData->GetDescriptorData()->SetRequest(request_array); } else { - sequence = (RUNTIME_sequence_t *) aData.GetDescriptorData()->GetSequence(); + sequence = (RUNTIME_sequence_t *) aData->GetDescriptorData()->GetSequence(); } - auto *CHAM_descC = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; + auto *CHAM_descC = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; //normal random generation of e -- ei~N(0, 1) to generate Z - auto *randomN = new T[n]; - LAPACKE_dlarnv(3, initial_seed, n, (double *) randomN); + auto *randomN = new T[full_problem_size]; + LAPACKE_dlarnv(3, initial_seed, full_problem_size, (double *) randomN); //Generate the co-variance matrix C auto *theta = new T[aConfigurations.GetInitialTheta().size()]; @@ -337,7 +371,7 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig int upper_lower = EXAGEOSTAT_LOWER; START_TIMING(matrix_gen_time); - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_descC, upper_lower, apLocation1, apLocation2, + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_descC, upper_lower, apLocation1, apLocation2, apLocation3, theta, aDistanceMetric, &aKernel); ExaGeoStatSequenceWait(sequence); STOP_TIMING(matrix_gen_time); @@ -345,16 +379,16 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig //Copy randomN to Z VERBOSE("Generate Normal Random Distribution Vector Z (Synthetic Dataset Generation Phase) .....") - auto *CHAM_descZ = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc; - CopyDescriptorZ(*aData.GetDescriptorData(), CHAM_descZ, randomN); + auto *CHAM_descZ = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc; + CopyDescriptorZ(*aData->GetDescriptorData(), CHAM_descZ, randomN); VERBOSE("Done.") //Cholesky factorization for the Co-variance matrix C VERBOSE("Cholesky factorization of Sigma (Synthetic Dataset Generation Phase) .....") START_TIMING(time_facto); - ExaGeoStatPotrfTile(EXAGEOSTAT_LOWER, CHAM_descC, aConfigurations.GetBand(), nullptr, nullptr, 0, 0); + ExaGeoStatPotrfTile(EXAGEOSTAT_LOWER, CHAM_descC, 0, nullptr, nullptr, 0, 0); STOP_TIMING(time_facto); - flops = flops + flops_dpotrf(n); + flops = flops + flops_dpotrf(full_problem_size); VERBOSE("Done.") //Triangular matrix-matrix multiplication @@ -363,17 +397,16 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig ExaGeoStatTrmmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_NO_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_descC, CHAM_descZ); STOP_TIMING(time_trmm); - flops = flops + flops_dtrmm(ChamLeft, n, CHAM_descZ->n); + flops = flops + flops_dtrmm(ChamLeft, full_problem_size, CHAM_descZ->n); VERBOSE("Done.") - if (aConfigurations.GetKernelName() == "UnivariateMaternNonGaussian") { + if (aConfigurations.GetIsNonGaussian()) { //Gaussian to non-gaussian transformation VERBOSE("Convert Z Gaussian to non-Gaussian (Synthetic Dataset Generation Phase) .....") - ExaGeoStatGaussianToNonTileAsync(*aData.GetDescriptorData(), CHAM_descZ, theta); + ExaGeoStatGaussianToNonTileAsync(*aData->GetDescriptorData(), CHAM_descZ, theta); VERBOSE("Done.") } delete[] theta; - const int P = aConfigurations.GetP(); if (aConfigurations.GetLogger()) { T *pMatrix; VERBOSE("Writing generated data to the disk (Synthetic Dataset Generation Phase) .....") @@ -388,7 +421,7 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig #else pMatrix = (T *) CHAM_descZ->mat; string path = aConfigurations.GetLoggerPath(); - helpers::DiskWriter::WriteVectorsToDisk(*pMatrix, n, P, path, *apLocation1); + helpers::DiskWriter::WriteVectorsToDisk(*pMatrix, full_problem_size, P, path, *apLocation1); #endif VERBOSE("Done.") } @@ -406,67 +439,66 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig results::Results::GetInstance()->SetTotalDataGenerationExecutionTime(time_facto + time_trmm); results::Results::GetInstance()->SetTotalDataGenerationFlops(total_flops); - results::Results::GetInstance()->SetGeneratedLocationsNumber(n); - results::Results::GetInstance()->SetGeneratedLocationsNumber(n); + results::Results::GetInstance()->SetGeneratedLocationsNumber(full_problem_size / aConfigurations.GetTimeSlot()); results::Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); results::Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); } template -T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(ExaGeoStatData &aData, T *apTheta, const int &aZMissNumber, - const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const ExaGeoStatHardware &aHardware, +T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, + const int &aZMissNumber, const int &aZObsNumber, T *apZObs, + T *apZActual, T *apZMiss, const ExaGeoStatHardware &aHardware, Configurations &aConfiguration, Locations &aMissLocations, Locations &aObsLocations, const kernels::Kernel &aKernel) { int i; this->SetContext(aHardware.GetChameleonContext()); - this->InitiatePredictionDescriptors(aConfiguration, aData); + this->InitiatePredictionDescriptors(aConfiguration, aData, aKernel.GetP()); double time_solve, mat_gen_time, time_gemm, time_mspe = 0.0, flops = 0.0; int num_params; - auto *CHAM_desc_Zmiss = aData.GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z_MISS).chameleon_desc; - auto *CHAM_desc_C12 = aData.GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_C12).chameleon_desc; - auto *CHAM_desc_C22 = aData.GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_C22).chameleon_desc; - auto *CHAM_desc_mspe = aData.GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_MSPE).chameleon_desc; - auto *CHAM_desc_mspe1 = aData.GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_MSPE_1).chameleon_desc; - auto *CHAM_desc_mspe2 = aData.GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_MSPE_2).chameleon_desc; - auto *CHAM_desc_Zactual = aData.GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z_Actual).chameleon_desc; - auto *CHAM_desc_Zobs = aData.GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z_OBSERVATIONS).chameleon_desc; - - T *mspe = aData.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe); + auto *CHAM_desc_Zmiss = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_Z_MISS).chameleon_desc; + auto *CHAM_desc_C12 = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_C12).chameleon_desc; + auto *CHAM_desc_C22 = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_C22).chameleon_desc; + auto *CHAM_desc_mspe = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_MSPE).chameleon_desc; + auto *CHAM_desc_mspe1 = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_MSPE_1).chameleon_desc; + auto *CHAM_desc_mspe2 = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_MSPE_2).chameleon_desc; + auto *CHAM_desc_Zactual = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_Z_Actual).chameleon_desc; + auto *CHAM_desc_Zobs = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_Z_OBSERVATIONS).chameleon_desc; + + T *mspe = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe); *mspe = 0; - T *mspe_1 = aData.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe1); + T *mspe_1 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe1); *mspe_1 = 0; - T *mspe_2 = aData.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe2); + T *mspe_2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe2); *mspe_2 = 0; auto kernel_name = aConfiguration.GetKernelName(); num_params = aKernel.GetParametersNumbers(); - auto median_locations = Locations(1, aData.GetLocations()->GetDimension()); - aData.CalculateMedianLocations(kernel_name, median_locations); + auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); + aData->CalculateMedianLocations(kernel_name, median_locations); // Create a Chameleon sequence, if not initialized before through the same descriptors RUNTIME_request_t request_array[2] = {RUNTIME_REQUEST_INITIALIZER, RUNTIME_REQUEST_INITIALIZER}; RUNTIME_sequence_t *sequence; - if (!aData.GetDescriptorData()->GetSequence()) { + if (!aData->GetDescriptorData()->GetSequence()) { ExaGeoStatCreateSequence(&sequence); - aData.GetDescriptorData()->SetSequence(sequence); - aData.GetDescriptorData()->SetRequest(request_array); + aData->GetDescriptorData()->SetSequence(sequence); + aData->GetDescriptorData()->SetRequest(request_array); } else { - sequence = (RUNTIME_sequence_t *) aData.GetDescriptorData()->GetSequence(); + sequence = (RUNTIME_sequence_t *) aData->GetDescriptorData()->GetSequence(); } - void *request = aData.GetDescriptorData()->GetRequest(); + void *request = aData->GetDescriptorData()->GetRequest(); //Copy data to vectors VERBOSE("Copy measurements vector to descZobs descriptor...") @@ -493,12 +525,12 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(ExaGeoStatData &aData, T START_TIMING(mat_gen_time); VERBOSE("Generate C22 Covariance Matrix... (Prediction Stage)") int upper_lower = EXAGEOSTAT_LOWER; - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_C22, upper_lower, &aObsLocations, + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_C22, upper_lower, &aObsLocations, &aObsLocations, &median_locations, apTheta, 0, &aKernel); ExaGeoStatSequenceWait(sequence); VERBOSE("Done.") VERBOSE("Generate C12 Covariance Matrix... (Prediction Stage)") - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_C12, upper_lower, &aMissLocations, + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_C12, upper_lower, &aMissLocations, &aObsLocations, &median_locations, apTheta, 0, &aKernel); ExaGeoStatSequenceWait(sequence); VERBOSE("Done.") @@ -524,9 +556,9 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(ExaGeoStatData &aData, T if (apZActual) { START_TIMING(time_mspe); VERBOSE("Calculate Mean Square Prediction Error (MSPE) ... (Prediction Stage)") - if (kernel_name == "BivariateMaternParsimonious" || kernel_name == "BivariateMaternParsimonious2" || - kernel_name == "BivariateMaternParsimoniousProfile") { - throw runtime_error("Bivariate Kernels are not supported yet."); + if (kernel_name == "BivariateMaternParsimonious") { + ExaGeoStatMLEMSPEBivariateTileAsync(CHAM_desc_Zactual, CHAM_desc_Zmiss, CHAM_desc_mspe1, CHAM_desc_mspe2, + CHAM_desc_mspe, sequence, &request_array[0]); } else { this->ExaGeoStatMLEMSPETileAsync(CHAM_desc_Zactual, CHAM_desc_Zmiss, CHAM_desc_mspe, sequence, request); } @@ -547,9 +579,9 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(ExaGeoStatData &aData, T "\n\n# of missing observations :%d\n\nPrediction Execution Time: %.8f, ""Flops: %.8f, Mean Square Prediction Error (MSPE): %.8f\n\n", aZMissNumber, (mat_gen_time + time_solve + time_mspe), (flops / 1e9 / (time_solve)), *mspe); } - LOGGER("- Z Actual .. Z Miss") + VERBOSE("- Z Actual .. Z Miss") for (i = 0; i < aZMissNumber; i++) { - LOGGER(" (" << apZActual[i] << ", " << apZMiss[i] << ")") + VERBOSE(" (" << apZActual[i] << ", " << apZMiss[i] << ")") } results::Results::GetInstance()->SetMSPEExecutionTime(time_solve + time_gemm); @@ -565,17 +597,220 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(ExaGeoStatData &aData, T } template -void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigurations, ExaGeoStatData &aData, +T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, + T *apTheta, const int &aZMissNumber, + const int &aZObsNumber, T *apZObs, T *apZActual, + T *apZMiss, + const hardware::ExaGeoStatHardware &aHardware, + configurations::Configurations &aConfiguration, + dataunits::Locations &aMissLocations, + dataunits::Locations &aObsLocations, + const kernels::Kernel &aKernel) { + + int i; + this->SetContext(aHardware.GetChameleonContext()); + this->InitiatePredictionDescriptors(aConfiguration, aData, aKernel.GetP()); + + double time_solve, mat_gen_time, time_mse, mat_gen_time_2, dposv_time, gemms_time, time_trsm, time_gemm, time_mspe = 0.0, flops = 0.0; + int num_params; + + auto *CHAM_desc_Zmiss = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_Z_MISS).chameleon_desc; + auto *CHAM_desc_C12 = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_C12).chameleon_desc; + auto *CHAM_desc_C22 = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_C22).chameleon_desc; + auto *CHAM_desc_mspe = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_MSPE).chameleon_desc; + auto *CHAM_desc_mspe1 = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_MSPE_1).chameleon_desc; + auto *CHAM_desc_mspe2 = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_MSPE_2).chameleon_desc; + auto *CHAM_desc_Zactual = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_Z_Actual).chameleon_desc; + auto *CHAM_desc_Zobs = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_Z_OBSERVATIONS).chameleon_desc; + auto *CHAM_desc_R = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_R).chameleon_desc; + auto *CHAM_desc_Rcopy = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_R_COPY).chameleon_desc; + auto *CHAM_desc_product = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_PRODUCT).chameleon_desc; + + T *mspe = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe); + *mspe = 0; + T *mspe_1 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe1); + *mspe_1 = 0; + T *mspe_2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe2); + *mspe_2 = 0; + + auto kernel_name = aConfiguration.GetKernelName(); + num_params = aKernel.GetParametersNumbers(); + auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); + aData->CalculateMedianLocations(kernel_name, median_locations); + + // Create a Chameleon sequence, if not initialized before through the same descriptors + RUNTIME_request_t request_array[2] = {RUNTIME_REQUEST_INITIALIZER, RUNTIME_REQUEST_INITIALIZER}; + RUNTIME_sequence_t *sequence; + if (!aData->GetDescriptorData()->GetSequence()) { + ExaGeoStatCreateSequence(&sequence); + aData->GetDescriptorData()->SetSequence(sequence); + aData->GetDescriptorData()->SetRequest(request_array); + } else { + sequence = (RUNTIME_sequence_t *) aData->GetDescriptorData()->GetSequence(); + } + void *request = aData->GetDescriptorData()->GetRequest(); + + //Copy data to vectors + VERBOSE("Copy measurements vector to descZobs descriptor...") + ExaGeoStatLap2Desc(apZObs, aZObsNumber, CHAM_desc_Zobs, UpperLower::EXAGEOSTAT_UPPER_LOWER); + VERBOSE("Done.") + + if (apZActual) { + //Copy data to vectors + VERBOSE("Copy actual measurements vector to descZactual descriptor...") + ExaGeoStatLap2Desc(apZActual, aZMissNumber, CHAM_desc_Zactual, UpperLower::EXAGEOSTAT_UPPER_LOWER); + VERBOSE("Done.") + } + + LOGGER("- Estimated Parameters (", true) + for (i = 0; i < num_params; i++) { + LOGGER_PRECISION(apTheta[i]) + if (i != num_params - 1) { + LOGGER_PRECISION(", ") + } + } + LOGGER_PRECISION(")") + LOGGER("") + + //Convert the non-Gaussian observation to Gaussian + this->ExaGeoStatNonGaussianTransformTileAsync(CHAM_desc_Zobs, CHAM_desc_product, apTheta, sequence, + &request_array[0]); + + START_TIMING(mat_gen_time); + VERBOSE("Generate R_theta Covariance Matrix... (Prediction Stage)") + int upper_lower = EXAGEOSTAT_LOWER; + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_C22, upper_lower, &aObsLocations, + &aObsLocations, &median_locations, apTheta, 0, &aKernel); + ExaGeoStatSequenceWait(sequence); + VERBOSE("Done.") + STOP_TIMING(mat_gen_time); + + START_TIMING(time_solve); + VERBOSE("Calculate dposv R_theta Covariance Matrix... (Prediction Stage)"); + ExaGeoStatPosvTile(EXAGEOSTAT_LOWER, CHAM_desc_C22, CHAM_desc_Zobs); + flops = flops + flops_dpotrf(aZObsNumber); + flops = flops + 2 * flops_dtrsm(ChamLeft, CHAM_desc_C22->m, CHAM_desc_Zobs->n); + VERBOSE("Done.") + STOP_TIMING(time_solve); + + START_TIMING(mat_gen_time_2); + VERBOSE("Generate R_theta Covariance Matrix... (Prediction Stage)") + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_R, upper_lower, &aObsLocations, + &aMissLocations, &median_locations, apTheta, 0, &aKernel); + ExaGeoStatSequenceWait(sequence); + VERBOSE("Done.") + STOP_TIMING(mat_gen_time_2); + + ExaGeoStatLapackCopyTile(EXAGEOSTAT_LOWER, CHAM_desc_R, CHAM_desc_Rcopy); + + START_TIMING(time_trsm); + VERBOSE("Calculate dposv r_theta Covariance Matrix... (Prediction Stage)"); + ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_NO_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_C22, + nullptr, nullptr, CHAM_desc_R, 0); + ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_C22, + nullptr, nullptr, CHAM_desc_R, 0); + flops = flops + 2 * flops_dtrsm(ChamLeft, CHAM_desc_C22->m, CHAM_desc_Zobs->n); + VERBOSE("Done.") + STOP_TIMING(time_trsm); + + START_TIMING(time_gemm); + VERBOSE("For each missing location, Generate correlation vector CHAMELEON_descr (Prediction Stage) ....."); + CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Rcopy, CHAM_desc_Zobs, 0, CHAM_desc_Zmiss); + CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Rcopy, CHAM_desc_R, 0, CHAM_desc_C12); + STOP_TIMING(time_gemm); + + auto r = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_C12); + auto Zmiss2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_Zmiss);; + + auto ng_nu = new T[aZMissNumber]; + auto ng_sigma_sq = new T[aZMissNumber]; + + double mu = -1; + double sigma_sq = -1; + + for (i = 0; i < aZMissNumber; i++) { + mu = Zmiss2[i]; + sigma_sq = 1 - r[i * aZMissNumber + i]; + ng_nu[i] = mu; + ng_sigma_sq[i] = sigma_sq; + //r^t X Z + apZMiss[i] = apTheta[2] + (apTheta[3] / (apTheta[4] * sqrt(1 - apTheta[5] * sigma_sq))) * + exp(apTheta[5] * pow(mu, 2) / (2 * (1 - apTheta[5] * sigma_sq))) * + (exp((pow(apTheta[4], 2) * sigma_sq + 2 * apTheta[4] * mu) / + (2 * (1 - apTheta[5] * sigma_sq))) - 1); + } + VERBOSE(" Done.") + + ExaGeoStatLap2Desc(apZMiss, aZMissNumber, CHAM_desc_Zmiss, EXAGEOSTAT_UPPER_LOWER); + + if (apZActual != nullptr) { + START_TIMING(time_mspe); + VERBOSE("Calculate Mean Square Error (MSE) ... (Prediction Stage) \n"); + this->ExaGeoStatMLEMSPETileAsync(CHAM_desc_Zactual, CHAM_desc_Zmiss, CHAM_desc_mspe, sequence, request); + ExaGeoStatSequenceWait(sequence); + VERBOSE(" Done.") + STOP_TIMING(time_mspe); + *mspe /= aZMissNumber; + } else { + *mspe = -1; + } + +#if defined(CHAMELEON_USE_MPI) + if(CHAMELEON_My_Mpi_Rank() == 0) + { +#endif + if (aConfiguration.GetLogger()) { + fprintf(aConfiguration.GetFileLogPath(), + "\n\n# of missing observations :%d\n\nPrediction Execution Time: %.8f, ""Flops: %.8f, Mean Square Prediction Error (MSPE): %.8f\n\n", + aZMissNumber, (mat_gen_time + mat_gen_time_2 + time_solve + time_mspe), (flops / 1e9 / (time_solve)), + *mspe); + } + VERBOSE("- Z Actual .. Z Miss") + for (i = 0; i < aZMissNumber; i++) { + VERBOSE(" (" << apZActual[i] << ", " << apZMiss[i] << ")") + } + + results::Results::GetInstance()->SetMSPEExecutionTime(time_solve + time_gemm + time_trsm); + results::Results::GetInstance()->SetMSPEFlops((flops / 1e9 / (time_solve + time_gemm))); + results::Results::GetInstance()->SetMSPEError(*mspe); + +#if defined(CHAMELEON_USE_MPI) + } +#endif + + T *all_mspe = new T[3]; + all_mspe[0] = *mspe; + all_mspe[1] = *mspe_1; + all_mspe[2] = *mspe_2; + delete[] ng_sigma_sq; + delete[] ng_nu; + return all_mspe; +} + +template +void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigurations, + std::unique_ptr> &aData, const ExaGeoStatHardware &aHardware, T *apTruthTheta, T *apEstimatedTheta, Locations &aMissLocations, Locations &aObsLocations, const kernels::Kernel &aKernel) { this->SetContext(aHardware.GetChameleonContext()); - this->InitiateMLOEMMOMDescriptors(aConfigurations, aData); + this->InitiateMLOEMMOMDescriptors(aConfigurations, aData, aKernel.GetP()); auto kernel_name = aConfigurations.GetKernelName(); - auto median_locations = Locations(1, aData.GetLocations()->GetDimension()); - aData.CalculateMedianLocations(kernel_name, median_locations); + auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); + aData->CalculateMedianLocations(kernel_name, median_locations); int n_z_miss = aConfigurations.GetUnknownObservationsNb(); int num_par = aKernel.GetParametersNumbers(); @@ -595,49 +830,53 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu auto loe = new T[n_z_miss]; auto mom = new T[n_z_miss]; - auto *CHAM_desc_k_t = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_k_T).chameleon_desc; - auto *CHAM_desc_k_a = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_k_A).chameleon_desc; - auto *CHAM_desc_K_t = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_K_T).chameleon_desc; - auto *CHAM_desc_K_a = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_K_A).chameleon_desc; - auto *CHAM_desc_k_a_tmp = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_k_A_TMP).chameleon_desc; - auto *CHAM_desc_k_t_tmp = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_k_T_TMP).chameleon_desc; - auto *CHAM_desc_expr1 = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_EXPR_1).chameleon_desc; - auto *CHAM_desc_expr2 = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_EXPR_2).chameleon_desc; - auto *CHAM_desc_expr3 = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_EXPR_3).chameleon_desc; - auto *CHAM_desc_expr4 = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_EXPR_4).chameleon_desc; - auto *CHAM_desc_mloe = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_MLOE).chameleon_desc; - auto *CHAM_desc_mmom = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_MMOM).chameleon_desc; - auto *CHAM_desc_estimated_alpha = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_TIMATED_ALPHA).chameleon_desc; - auto *CHAM_desc_truth_alpha = aData.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_TRUTH_ALPHA).chameleon_desc; - - T *mloe = aData.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mloe); + auto *CHAM_desc_k_t = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_k_T).chameleon_desc; + auto *CHAM_desc_k_a = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_k_A).chameleon_desc; + auto *CHAM_desc_K_t = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_K_T).chameleon_desc; + auto *CHAM_desc_K_a = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_K_A).chameleon_desc; + auto *CHAM_desc_k_a_tmp = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_k_A_TMP).chameleon_desc; + auto *CHAM_desc_k_t_tmp = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_k_T_TMP).chameleon_desc; + auto *CHAM_desc_expr1 = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_EXPR_1).chameleon_desc; + auto *CHAM_desc_expr2 = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_EXPR_2).chameleon_desc; + auto *CHAM_desc_expr3 = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_EXPR_3).chameleon_desc; + auto *CHAM_desc_expr4 = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_EXPR_4).chameleon_desc; + auto *CHAM_desc_mloe = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_MLOE).chameleon_desc; + auto *CHAM_desc_mmom = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_MMOM).chameleon_desc; + auto *CHAM_desc_estimated_alpha = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_TIMATED_ALPHA).chameleon_desc; + auto *CHAM_desc_truth_alpha = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_TRUTH_ALPHA).chameleon_desc; + + T *mloe = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mloe); *mloe = 0; - T *mmom = aData.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mmom); + T *mmom = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mmom); *mmom = 0; // Create a Chameleon sequence, if not initialized before through the same descriptors RUNTIME_request_t request_array[2] = {RUNTIME_REQUEST_INITIALIZER, RUNTIME_REQUEST_INITIALIZER}; RUNTIME_sequence_t *sequence; - if (!aData.GetDescriptorData()->GetSequence()) { + if (!aData->GetDescriptorData()->GetSequence()) { ExaGeoStatCreateSequence(&sequence); - aData.GetDescriptorData()->SetSequence(sequence); - aData.GetDescriptorData()->SetRequest(request_array); + aData->GetDescriptorData()->SetSequence(sequence); + aData->GetDescriptorData()->SetRequest(request_array); } else { - sequence = (RUNTIME_sequence_t *) aData.GetDescriptorData()->GetSequence(); + sequence = (RUNTIME_sequence_t *) aData->GetDescriptorData()->GetSequence(); } - void *request = aData.GetDescriptorData()->GetRequest(); + void *request = aData->GetDescriptorData()->GetRequest(); - auto lmiss = new Locations(n_z_miss, aData.GetLocations()->GetDimension()); + auto lmiss = new Locations(n_z_miss, aData->GetLocations()->GetDimension()); T nu12; T rho; T sigma_square12; @@ -680,19 +919,13 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu this->ExaGeoStatLap2Desc(truth_alpha, m, CHAM_desc_truth_alpha, EXAGEOSTAT_UPPER_LOWER); this->ExaGeoStatLap2Desc(estimated_alpha, m, CHAM_desc_estimated_alpha, EXAGEOSTAT_UPPER_LOWER); - if (kernel_name == "BivariateMaternParsimonious2" || - kernel_name == "BivariateMaternParsimonious2Profile") { - //// TODO:not implemented in C - throw runtime_error("Selected Kernel Is Not Supported!"); - } - START_TIMING(matrix_gen); VERBOSE("Create K_a and K_t Covariance Matrices (MLOE-MMOM).....") int upper_lower = EXAGEOSTAT_LOWER; - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_K_a, upper_lower, &aObsLocations, + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_K_a, upper_lower, &aObsLocations, &aObsLocations, &median_locations, apEstimatedTheta, 0, &aKernel); this->ExaGeoStatSequenceWait(sequence); - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_K_t, upper_lower, &aObsLocations, + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_K_t, upper_lower, &aObsLocations, &aObsLocations, &median_locations, apTruthTheta, 0, &aKernel); this->ExaGeoStatSequenceWait(sequence); VERBOSE("Done.") @@ -728,9 +961,9 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu } START_TIMING(vecs_gen); upper_lower = EXAGEOSTAT_UPPER_LOWER; - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_k_t, upper_lower, &aObsLocations, lmiss, + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_k_t, upper_lower, &aObsLocations, lmiss, &median_locations, apTruthTheta, 0, &aKernel); - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_k_a, upper_lower, &aObsLocations, lmiss, + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_k_a, upper_lower, &aObsLocations, lmiss, &median_locations, apEstimatedTheta, 0, &aKernel); this->ExaGeoStatSequenceWait(sequence); @@ -787,7 +1020,6 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu } STOP_TIMING(trsm3); - START_TIMING(trsm4); // Triangular Solve (TRSM) k_t = TRSM(L_t^-T, k_t) if (verbose) { @@ -862,13 +1094,13 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu ExaGeoStatGeaddTile(EXAGEOSTAT_NO_TRANS, 1, CHAM_desc_truth_alpha, -1, CHAM_desc_expr3); ExaGeoStatGeaddTile(EXAGEOSTAT_NO_TRANS, 1, CHAM_desc_estimated_alpha, -1, CHAM_desc_expr4); - LOGGER("- Matrix Generation Time: " << matrix_gen << " Vectors Generation Time: " << vecs_gen - << " First Cholesky factorization Time: " << cholesky1 - << " First Cholesky factorization Time: " << cholesky2) - LOGGER("- First Trsm time: " << trsm1 << " Second Trsm time: " << trsm2 << " Third Trsm time: " << trsm3 - << " Fourth Trsm time: " << trsm4) - LOGGER("- First gemm time: " << gevv1 << " Second gemm time: " << gevv2 << " Third gemm time: " << gevv3 - << " Fourth gemm time: " << gevv4 << " Fifth gemm time: " << gevv5) + VERBOSE("- Matrix Generation Time: " << matrix_gen << " Vectors Generation Time: " << vecs_gen + << " First Cholesky factorization Time: " << cholesky1 + << " First Cholesky factorization Time: " << cholesky2) + VERBOSE("- First Trsm time: " << trsm1 << " Second Trsm time: " << trsm2 << " Third Trsm time: " << trsm3 + << " Fourth Trsm time: " << trsm4) + VERBOSE("- First gemm time: " << gevv1 << " Second gemm time: " << gevv2 << " Third gemm time: " << gevv3 + << " Fourth gemm time: " << gevv4 << " Fifth gemm time: " << gevv5) ExaGeoStatMLETileAsyncMLOEMMOM(CHAM_desc_expr2, CHAM_desc_expr3, CHAM_desc_expr4, CHAM_desc_mloe, CHAM_desc_mmom, sequence, request); this->ExaGeoStatSequenceWait(sequence); @@ -902,53 +1134,54 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu template T *LinearAlgebraMethods::ExaGeoStatFisherTile(configurations::Configurations &aConfigurations, - dataunits::ExaGeoStatData &aData, + std::unique_ptr> &aData, const hardware::ExaGeoStatHardware &aHardware, T *apTheta, const kernels::Kernel &aKernel) { this->SetContext(aHardware.GetChameleonContext()); - this->InitiateFisherDescriptors(aConfigurations, *aData.GetDescriptorData()); - - auto *CHAM_desc_A = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_A).chameleon_desc; - auto *CHAM_desc_C = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_C).chameleon_desc; - auto *CHAM_desc_CJ = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_CJ).chameleon_desc; - auto *CHAM_desc_results = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_RESULTS).chameleon_desc; - auto *CHAM_desc_CK = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_CK).chameleon_desc; - auto *CHAM_desc_C_diag = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_C_DIAG).chameleon_desc; - auto *CHAM_desc_C_trace = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_C_TRACE).chameleon_desc; - - auto trace = aData.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_C_trace); + this->InitiateFisherDescriptors(aConfigurations, *aData->GetDescriptorData()); + + auto *CHAM_desc_A = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_A).chameleon_desc; + auto *CHAM_desc_C = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_C).chameleon_desc; + auto *CHAM_desc_CJ = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_CJ).chameleon_desc; + auto *CHAM_desc_results = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_RESULTS).chameleon_desc; + auto *CHAM_desc_CK = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_CK).chameleon_desc; + auto *CHAM_desc_C_diag = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_C_DIAG).chameleon_desc; + auto *CHAM_desc_C_trace = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_C_TRACE).chameleon_desc; + + auto trace = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_C_trace); *trace = 0.0; RUNTIME_request_t request_array[2] = {RUNTIME_REQUEST_INITIALIZER, RUNTIME_REQUEST_INITIALIZER}; RUNTIME_sequence_t *sequence; - if (!aData.GetDescriptorData()->GetSequence()) { + if (!aData->GetDescriptorData()->GetSequence()) { ExaGeoStatCreateSequence(&sequence); - aData.GetDescriptorData()->SetSequence(sequence); - aData.GetDescriptorData()->SetRequest(request_array); + aData->GetDescriptorData()->SetSequence(sequence); + aData->GetDescriptorData()->SetRequest(request_array); } else { - sequence = (RUNTIME_sequence_t *) aData.GetDescriptorData()->GetSequence(); + sequence = (RUNTIME_sequence_t *) aData->GetDescriptorData()->GetSequence(); } - void *request = aData.GetDescriptorData()->GetRequest(); + void *request = aData->GetDescriptorData()->GetRequest(); auto kernel_name = aConfigurations.GetKernelName(); int num_params = aKernel.GetParametersNumbers(); - auto median_locations = Locations(1, aData.GetLocations()->GetDimension()); - aData.CalculateMedianLocations(kernel_name, median_locations); + auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); + aData->CalculateMedianLocations(kernel_name, median_locations); double time = 0.0; START_TIMING(time); VERBOSE("Generate covariance matrix CHAM_desc_C (Fisher Matrix Generation).....") - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_C, EXAGEOSTAT_LOWER, aData.GetLocations(), - aData.GetLocations(), &median_locations, apTheta, aConfigurations.GetDistanceMetric(), + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_C, EXAGEOSTAT_LOWER, aData->GetLocations(), + aData->GetLocations(), &median_locations, apTheta, + aConfigurations.GetDistanceMetric(), &aKernel); ExaGeoStatSequenceWait(sequence); VERBOSE("Done.") @@ -972,11 +1205,12 @@ T *LinearAlgebraMethods::ExaGeoStatFisherTile(configurations::Configurations kernel_name = "UnivariateMaternNuggetsStationary"; } - auto pKernel_cj = plugins::PluginRegistry>::Create(kernel_name); + auto pKernel_cj = plugins::PluginRegistry>::Create(kernel_name, + aConfigurations.GetTimeSlot()); VERBOSE("Generate covariance matrix CHAM_desc_CJ (Fisher Matrix Generation).....") - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_CJ, EXAGEOSTAT_UPPER_LOWER, - aData.GetLocations(), aData.GetLocations(), &median_locations, apTheta, + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_CJ, EXAGEOSTAT_UPPER_LOWER, + aData->GetLocations(), aData->GetLocations(), &median_locations, apTheta, aConfigurations.GetDistanceMetric(), pKernel_cj); ExaGeoStatSequenceWait(sequence); VERBOSE("Done.") @@ -1003,11 +1237,12 @@ T *LinearAlgebraMethods::ExaGeoStatFisherTile(configurations::Configurations kernel_name = "UnivariateMaternNuggetsStationary"; } - auto pKernel_ck = plugins::PluginRegistry>::Create(kernel_name); + auto pKernel_ck = plugins::PluginRegistry>::Create(kernel_name, + aConfigurations.GetTimeSlot()); VERBOSE("Generate covariance matrix CHAM_desc_CK (Fisher Matrix Generation).....") - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_CK, EXAGEOSTAT_UPPER_LOWER, - aData.GetLocations(), aData.GetLocations(), &median_locations, apTheta, + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_CK, EXAGEOSTAT_UPPER_LOWER, + aData->GetLocations(), aData->GetLocations(), &median_locations, apTheta, aConfigurations.GetDistanceMetric(), pKernel_ck); ExaGeoStatSequenceWait(sequence); VERBOSE("Done.") @@ -1072,9 +1307,9 @@ T *LinearAlgebraMethods::ExaGeoStatFisherTile(configurations::Configurations I_matrix[num_params * num_params] = time; results::Results::GetInstance()->SetTotalFisherTime(time); - results::Results::GetInstance()->SetFisher00((double) I_matrix[0]); - results::Results::GetInstance()->SetFisher11((double) I_matrix[4]); - results::Results::GetInstance()->SetFisher22((double) I_matrix[8]); + results::Results::GetInstance()->SetFisher00((T) I_matrix[0]); + results::Results::GetInstance()->SetFisher11((T) I_matrix[4]); + results::Results::GetInstance()->SetFisher22((T) I_matrix[8]); delete[] A; return I_matrix; @@ -1083,11 +1318,11 @@ T *LinearAlgebraMethods::ExaGeoStatFisherTile(configurations::Configurations template void LinearAlgebraMethods::ExaGeoStatGetZObs(Configurations &aConfigurations, T *apZ, const int &aSize, - DescriptorData &aDescData, T *apMeasurementsMatrix) { + DescriptorData &aDescData, T *apMeasurementsMatrix, const int &aP) { auto z_desc = (CHAM_desc_t *) aDescData.GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_COPY).chameleon_desc; if (!z_desc) { - int n = aConfigurations.GetProblemSize(); + int full_problem_size = aConfigurations.GetProblemSize() * aP; int dts = aConfigurations.GetDenseTileSize(); int p_grid = aConfigurations.GetPGrid(); int q_grid = aConfigurations.GetQGrid(); @@ -1104,7 +1339,7 @@ LinearAlgebraMethods::ExaGeoStatGetZObs(Configurations &aConfigurations, T *a throw runtime_error("Unsupported for now!"); } aDescData.SetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_COPY, is_OOC, apMeasurementsMatrix, float_point, dts, - dts, dts * dts, n, 1, 0, 0, n, 1, p_grid, q_grid); + dts, dts * dts, full_problem_size, 1, 0, 0, full_problem_size, 1, p_grid, q_grid); z_desc = (CHAM_desc_t *) aDescData.GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_COPY).chameleon_desc; } this->ExaGeoStatDesc2Lap(apZ, aSize, z_desc, UpperLower::EXAGEOSTAT_UPPER_LOWER); @@ -1365,6 +1600,81 @@ LinearAlgebraMethods::CopyDescriptorZ(DescriptorData &aDescriptorData, voi } +template +int +LinearAlgebraMethods::ExaGeoStatMLEMSPEBivariateTileAsync(void *apDescZPre, void *apDescZMiss, void *apDescsError1, + void *apDescsError2, void *apDescsError, + void *apSequence, void *apRequest) { + // Check for initialize the Chameleon context. + if (!this->mpContext) { + throw std::runtime_error( + "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); + } + + RUNTIME_option_t options; + RUNTIME_options_init(&options, (chameleon_context_s *) this->mpContext, + (RUNTIME_sequence_t *) apSequence, + (RUNTIME_request_t *) apRequest); + + int m, m0; + int tempmm; + auto Zpre = (CHAM_desc_t *) apDescZPre; + + struct starpu_codelet *cl = &cl_dmse_bivariate; + + + for (m = 0; m < Zpre->mt; m++) { + tempmm = m == Zpre->mt - 1 ? Zpre->m - m * Zpre->mb : Zpre->mb; + m0 = m * Zpre->mb; + starpu_insert_task(cl, + STARPU_VALUE, &tempmm, sizeof(int), + STARPU_VALUE, &m0, sizeof(int), + STARPU_RW, ExaGeoStatDataGetAddr(apDescsError1, 0, 0), + STARPU_RW, ExaGeoStatDataGetAddr(apDescsError2, 0, 0), + STARPU_RW, ExaGeoStatDataGetAddr(apDescsError, 0, 0), + STARPU_R, ExaGeoStatDataGetAddr(apDescZPre, m, 0), + STARPU_R, ExaGeoStatDataGetAddr(apDescZMiss, m, 0), +#if defined(CHAMELEON_CODELETS_HAVE_NAME) + STARPU_NAME, "dmse_bivariate", +#endif + 0); + } + this->ExaGeoStatOptionsFree(&options); + this->ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); + return CHAMELEON_SUCCESS; +} + +#ifdef USE_HICMA + +template +void LinearAlgebraMethods::CopyDescriptors(void *apSourceDesc, void *apDestinationDesc, const int &aSize, + const common::CopyDirection &aDirection) { + auto *z = new T[aSize]; + int status; + if (aDirection == common::CHAMELEON_TO_HICMA) { + status = CHAMELEON_Desc2Lap((cham_uplo_t) EXAGEOSTAT_UPPER_LOWER, (CHAM_desc_t *) apSourceDesc, z, aSize); + if (status != CHAMELEON_SUCCESS) { + throw std::runtime_error("CHAMELEON_Desc2Lap Failed!"); + } + status = HICMA_Lapack_to_Tile(z, aSize, (HICMA_desc_t *) apDestinationDesc); + if (status != HICMA_SUCCESS) { + throw std::runtime_error("HICMA_Lapack_to_Tile Failed!"); + } + } else if (aDirection == common::HICMA_TO_CHAMELEON) { + status = HICMA_Tile_to_Lapack((HICMA_desc_t *) apSourceDesc, z, aSize); + if (status != HICMA_SUCCESS) { + throw std::runtime_error("HICMA_Tile_to_Lapack Failed!"); + } + status = CHAMELEON_Lap2Desc((cham_uplo_t) EXAGEOSTAT_UPPER_LOWER, z, aSize, (CHAM_desc_t *) apDestinationDesc); + if (status != CHAMELEON_SUCCESS) { + throw std::runtime_error("CHAMELEON_Lap2Desc Failed!"); + } + } + delete[] z; +} + +#endif + template void LinearAlgebraMethods::ExaGeoStatDesc2Lap(T *apA, const int &aLDA, void *apDescA, const UpperLower &aUpperLower) { @@ -1375,6 +1685,15 @@ LinearAlgebraMethods::ExaGeoStatDesc2Lap(T *apA, const int &aLDA, void *apDes } +template +void +LinearAlgebraMethods::ExaGeoStatLap2Desc(T *apA, const int &aLDA, void *apDescA, const UpperLower &aUpperLower) { + int status = CHAMELEON_Lap2Desc((cham_uplo_t) aUpperLower, apA, aLDA, (CHAM_desc_t *) apDescA); + if (status != CHAMELEON_SUCCESS) { + throw std::runtime_error("CHAMELEON_Lap2Desc Failed!"); + } +} + template void LinearAlgebraMethods::ExaGeoStatLaSetTile(const common::UpperLower &aUpperLower, T alpha, T beta, void *apDescriptor) { diff --git a/src/linear-algebra-solvers/concrete/CMakeLists.txt b/src/linear-algebra-solvers/concrete/CMakeLists.txt index 0174fa63..bc7b3e61 100644 --- a/src/linear-algebra-solvers/concrete/CMakeLists.txt +++ b/src/linear-algebra-solvers/concrete/CMakeLists.txt @@ -19,7 +19,7 @@ set(SOURCES ${SOURCES} ) -if (EXAGEOSTAT_USE_HICMA) +if (USE_HICMA) list(APPEND SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tile-low-rank/HicmaImplementation.cpp ) diff --git a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp index c2236051..04b065ca 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp @@ -59,26 +59,109 @@ int ChameleonImplementation::ExaGeoStatDoubleDotProduct(void *apDescA, void * return CHAMELEON_SUCCESS; } +template +int ChameleonImplementation::ExaGeoStatNonGaussianLogLikeTileAsync(void *apDescZ, void *apDescSum, const T *apTheta, void *apSequence, void *apRequest) { + + // Check for initialize the Chameleon context. + if (!this->mpContext) { + throw std::runtime_error( + "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); + } + + RUNTIME_option_t options; + this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); + + int m, m0; + int temp; + auto Z = (CHAM_desc_t *) apDescZ; + auto A = (CHAM_desc_t *) apDescSum; + struct starpu_codelet *cl = &this->cl_non_gaussian_loglike; + + + for (m = 0; m < Z->mt; m++) { + temp = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; + m0 = m * Z->mb; + + starpu_insert_task(cl, + STARPU_VALUE, &temp, sizeof(int), + STARPU_VALUE, &m0, sizeof(int), + STARPU_R, ExaGeoStatDataGetAddr(Z, m, 0), + STARPU_RW, ExaGeoStatDataGetAddr(A, 0, 0), + STARPU_VALUE, &apTheta[0], sizeof(double), + STARPU_VALUE, &apTheta[1], sizeof(double), + STARPU_VALUE, &apTheta[2], sizeof(double), + STARPU_VALUE, &apTheta[3], sizeof(double), + STARPU_VALUE, &apTheta[4], sizeof(double), + STARPU_VALUE, &apTheta[5], sizeof(double), + 0); + } + + this->ExaGeoStatOptionsFree(&options); + this->ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); + return CHAMELEON_SUCCESS; +} + +template +int +ChameleonImplementation::ExaGeoStatNonGaussianTransformTileAsync(void *apDescZ, void *apDescFlag, const T *apTheta, void *apSequence, + void *apRequest) { + // Check for initialize the Chameleon context. + if (!this->mpContext) { + throw std::runtime_error( + "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); + } + + RUNTIME_option_t options; + this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); + + int m, m0; + int tempmm; + auto Z = (CHAM_desc_t *) apDescZ; + struct starpu_codelet *cl = &this->cl_non_gaussian_transform; + + + for (m = 0; m < Z->mt; m++) { + tempmm = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; + m0 = m * Z->mb; + + starpu_insert_task(cl, + STARPU_VALUE, &tempmm, sizeof(int), + STARPU_VALUE, &m0, sizeof(int), + STARPU_RW, ExaGeoStatDataGetAddr(apDescZ, m, 0), + STARPU_W, ExaGeoStatDataGetAddr(apDescFlag, 0, 0), + STARPU_VALUE, &apTheta[0], sizeof(double), + STARPU_VALUE, &apTheta[1], sizeof(double), + STARPU_VALUE, &apTheta[2], sizeof(double), + STARPU_VALUE, &apTheta[3], sizeof(double), + STARPU_VALUE, &apTheta[4], sizeof(double), + STARPU_VALUE, &apTheta[5], sizeof(double), + 0); + } + + this->ExaGeoStatOptionsFree(&options); + this->ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); + return CHAMELEON_SUCCESS; +} template -T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, ExaGeoStatData &aData, +T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) { this->SetContext(aHardware.GetContext(aConfigurations.GetComputation())); - if (!aData.GetDescriptorData()->GetIsDescriptorInitiated()) { - this->InitiateDescriptors(aConfigurations, *aData.GetDescriptorData(), apMeasurementsMatrix); + if (!aData->GetDescriptorData()->GetIsDescriptorInitiated()) { + this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(), aKernel.GetP(), apMeasurementsMatrix); } // Create a Chameleon sequence, if not initialized before through the same descriptors RUNTIME_request_t request_array[2] = {RUNTIME_REQUEST_INITIALIZER, RUNTIME_REQUEST_INITIALIZER}; - if (!aData.GetDescriptorData()->GetSequence()) { + if (!aData->GetDescriptorData()->GetSequence()) { RUNTIME_sequence_t *sequence; this->ExaGeoStatCreateSequence(&sequence); - aData.GetDescriptorData()->SetSequence(sequence); - aData.GetDescriptorData()->SetRequest(request_array); + aData->GetDescriptorData()->SetSequence(sequence); + aData->GetDescriptorData()->SetRequest(request_array); } - auto pSequence = (RUNTIME_sequence_t *) aData.GetDescriptorData()->GetSequence(); + auto pSequence = (RUNTIME_sequence_t *) aData->GetDescriptorData()->GetSequence(); //Initialization T loglik = 0.0, logdet, variance, variance1 = 1, variance2 = 1, variance3, dot_product = 0, dot_product1 = 0, dot_product2 = 0, dot_product3, n, dzcpy_time, time_facto, time_solve, logdet_calculate, matrix_gen_time; double accumulated_executed_time, accumulated_flops; @@ -89,47 +172,54 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa auto kernel_name = aConfigurations.GetKernelName(); int num_params = aKernel.GetParametersNumbers(); - auto median_locations = Locations(1, aData.GetLocations()->GetDimension()); - aData.CalculateMedianLocations(kernel_name, median_locations); + auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); + aData->CalculateMedianLocations(kernel_name, median_locations); - auto *CHAM_desc_C = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_C = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_C).chameleon_desc; - auto *CHAM_desc_sub_C11 = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_sub_C11 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_C11).chameleon_desc; - auto *CHAM_desc_sub_C12 = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_sub_C12 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_C12).chameleon_desc; - auto *CHAM_desc_sub_C22 = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_sub_C22 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_C22).chameleon_desc; - auto *CHAM_desc_Z = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_Z = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_Z).chameleon_desc; - auto *CHAM_desc_Z1 = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_Z1 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_Z_1).chameleon_desc; - auto *CHAM_desc_Z2 = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_Z2 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_Z_2).chameleon_desc; - auto *CHAM_desc_Z3 = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_Z3 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_Z_3).chameleon_desc; - auto *CHAM_desc_Zcpy = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_Zcpy = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_Z_COPY).chameleon_desc; - auto *CHAM_desc_det = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_det = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_DETERMINANT).chameleon_desc; - auto *CHAM_desc_product = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_PRODUCT).chameleon_desc; - auto *CHAM_desc_product1 = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_product = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_PRODUCT).chameleon_desc; + auto *CHAM_desc_sum = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_SUM).chameleon_desc; + auto *CHAM_desc_product1 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_PRODUCT_1).chameleon_desc; - auto *CHAM_desc_product2 = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_product2 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_PRODUCT_2).chameleon_desc; - auto *CHAM_desc_product3 = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + auto *CHAM_desc_product3 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_PRODUCT_3).chameleon_desc; - T *determinant = aData.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_det); + T *determinant = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_det); *determinant = 0; - T *product = aData.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_product); + T *product = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_product); *product = 0; + T *sum ; + if(aConfigurations.GetIsNonGaussian()) { + sum = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_sum); + *sum = 0; + } n = CHAM_desc_C->m; nhrs = CHAM_desc_Z->n; string recovery_file = aConfigurations.GetRecoveryFile(); - int iter_count = aData.GetMleIterations(); + int iter_count = aData->GetMleIterations(); if (recovery_file.empty() || !(this->recover((char *) (recovery_file.c_str()), iter_count, (T *) theta, &loglik, num_params))) { @@ -162,8 +252,8 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa int distance_metric = aConfigurations.GetDistanceMetric(); - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_sub_C11, upper_lower, aData.GetLocations(), - aData.GetLocations(), &median_locations, univariate_theta, distance_metric, + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_sub_C11, upper_lower, aData->GetLocations(), + aData->GetLocations(), &median_locations, univariate_theta, distance_metric, &aKernel); nu12 = 0.5 * (theta[3] + theta[4]); @@ -174,8 +264,8 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa univariate2_theta[1] = theta[2]; univariate2_theta[2] = nu12; - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_sub_C12, upper_lower, &median_locations, - aData.GetLocations(), &median_locations, univariate2_theta, 0, &aKernel); + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_sub_C12, upper_lower, &median_locations, + aData->GetLocations(), &median_locations, univariate2_theta, 0, &aKernel); STOP_TIMING(matrix_gen_time); VERBOSE("Done.") @@ -183,12 +273,12 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa univariate3_theta[1] = theta[2]; univariate3_theta[2] = theta[4]; - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_sub_C22, upper_lower, &median_locations, - aData.GetLocations(), &median_locations, univariate2_theta, 0, &aKernel); + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_sub_C22, upper_lower, &median_locations, + aData->GetLocations(), &median_locations, univariate2_theta, 0, &aKernel); } else { int upper_lower = EXAGEOSTAT_LOWER; - this->CovarianceMatrixCodelet(*aData.GetDescriptorData(), CHAM_desc_C, upper_lower, aData.GetLocations(), - aData.GetLocations(), &median_locations, (T *) theta, 0, &aKernel); + this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_C, upper_lower, aData->GetLocations(), + aData->GetLocations(), &median_locations, (T *) theta, 0, &aKernel); } this->ExaGeoStatSequenceWait(pSequence); STOP_TIMING(matrix_gen_time); @@ -211,6 +301,20 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa STOP_TIMING(logdet_calculate); VERBOSE("Done.") + if(aConfigurations.GetIsNonGaussian()){ + VERBOSE("Transform Z vector to Gaussian field ...") + this->ExaGeoStatNonGaussianTransformTileAsync(CHAM_desc_Z, CHAM_desc_product, (T *) theta, pSequence, &request_array[0]); + this->ExaGeoStatSequenceWait(pSequence); + VERBOSE(" Done.") + + CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Z, CHAM_desc_Z, 0, CHAM_desc_product); + + VERBOSE("Calculate non-Gaussian loglik ...") + this->ExaGeoStatNonGaussianLogLikeTileAsync(CHAM_desc_Z, CHAM_desc_sum, (T *) theta, pSequence, &request_array[0]); + this->ExaGeoStatSequenceWait(pSequence); + VERBOSE(" Done.\n") + } + // Solving Linear System (L*X=Z)--->inv(L)*Z VERBOSE("Solving the linear system ...") START_TIMING(time_solve); @@ -224,6 +328,7 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa VERBOSE("Calculating the MLE likelihood function ...") ExaGeoStatDoubleDotProduct(CHAM_desc_Z, CHAM_desc_product, pSequence, request_array); ExaGeoStatSequenceWait(pSequence); + if (kernel_name == "BivariateMaternParsimonious2Profile") { loglik = -(n / 2) + (n / 2) * log(n) - (n / 2) * log(dot_product) - 0.5 * logdet - (T) (n / 2.0) * log(2.0 * PI); @@ -254,7 +359,12 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa variance2 = (1.0 / (n / 3.0)) * dot_product2; } else { dot_product = *product; - loglik = -0.5 * dot_product - 0.5 * logdet - (double) (n / 2.0) * log(2.0 * PI); + loglik = -0.5 * dot_product - 0.5 * logdet; + if(aConfigurations.GetIsNonGaussian()){ + loglik = loglik - *sum - n * log(theta[3]) - (double) (n / 2.0) * log(2.0 * PI); + } else { + loglik = loglik - (double) (n / 2.0) * log(2.0 * PI); + } } VERBOSE("Done.") @@ -301,7 +411,7 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa LOGGER(" ---- Total Time: " << time_facto + logdet_calculate + time_solve) LOGGER(" ---- Gflop/s: " << flops / 1e9 / (time_facto + time_solve)) - aData.SetMleIterations(aData.GetMleIterations() + 1); + aData->SetMleIterations(aData->GetMleIterations() + 1); // for experiments and benchmarking accumulated_executed_time = @@ -316,19 +426,9 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa results::Results::GetInstance()->SetMaximumTheta(vector(theta, theta + num_params)); results::Results::GetInstance()->SetLogLikValue(loglik); - aConfigurations.SetEstimatedTheta(aConfigurations.GetStartingTheta()); return loglik; } -template -void -ChameleonImplementation::ExaGeoStatLap2Desc(T *apA, const int &aLDA, void *apDescA, const UpperLower &aUpperLower) { - int status = CHAMELEON_Lap2Desc((cham_uplo_t) aUpperLower, apA, aLDA, (CHAM_desc_t *) apDescA); - if (status != CHAMELEON_SUCCESS) { - throw std::runtime_error("CHAMELEON_Lap2Desc Failed!"); - } -} - template void ChameleonImplementation::ExaGeoStatLapackCopyTile(const common::UpperLower &aUpperLower, void *apA, void *apB) { int status = CHAMELEON_dlacpy_Tile((cham_uplo_t) aUpperLower, (CHAM_desc_t *) apA, (CHAM_desc_t *) apB); diff --git a/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp index c8a2b852..30608e74 100644 --- a/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp @@ -24,15 +24,15 @@ using namespace exageostat::helpers; using namespace exageostat::hardware; using namespace exageostat::configurations; -//// TODO: These variables are required to avoid undefined reference to HiCMA global variables. int store_only_diagonal_tiles = 1; int use_scratch = 1; int global_check = 0; //used to create dense matrix for accuracy check template -void HicmaImplementation::SetModelingDescriptors(ExaGeoStatData &aData, Configurations &aConfigurations) { +void HicmaImplementation::SetModelingDescriptors(std::unique_ptr> &aData, + Configurations &aConfigurations, const int &aP) { - int N = aConfigurations.GetProblemSize(); + int full_problem_size = aConfigurations.GetProblemSize() * aP; int lts = aConfigurations.GetLowTileSize(); int p_grid = aConfigurations.GetPGrid(); int q_grid = aConfigurations.GetQGrid(); @@ -51,55 +51,69 @@ void HicmaImplementation::SetModelingDescriptors(ExaGeoStatData &aData, Co int MBD = lts; int NBD = lts; - int MD = N; + int MD = full_problem_size; int ND = MBD; - aData.GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_CD, is_OOC, nullptr, float_point, MBD, - NBD, MBD * NBD, MD, ND, 0, 0, MD, ND, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_CD, is_OOC, nullptr, float_point, + MBD, + NBD, MBD * NBD, MD, ND, 0, 0, MD, ND, p_grid, q_grid); int MBUV = lts; int NBUV = 2 * max_rank; int MUV; - int N_over_lts_times_lts = N / lts * lts; - if (N_over_lts_times_lts < N) { + int N_over_lts_times_lts = full_problem_size / lts * lts; + if (N_over_lts_times_lts < full_problem_size) { MUV = N_over_lts_times_lts + lts; - } else if (N_over_lts_times_lts == N) { + } else if (N_over_lts_times_lts == full_problem_size) { MUV = N_over_lts_times_lts; } else { throw runtime_error("This case can't happens, N need to be >= lts*lts"); } int expr = MUV / lts; int NUV = 2 * expr * max_rank; - aData.GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_CUV, is_OOC, nullptr, float_point, - MBUV, NBUV, MBUV * NBUV, MUV, NUV, 0, 0, MUV, NUV, p_grid, q_grid); - auto *HICMA_descCUV = aData.GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, - DescriptorName::DESCRIPTOR_CUV).hicma_desc; + aData->GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_CUV, is_OOC, nullptr, float_point, + MBUV, NBUV, MBUV * NBUV, MUV, NUV, 0, 0, MUV, NUV, p_grid, q_grid); + auto *HICMA_descCUV = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, + DescriptorName::DESCRIPTOR_CUV).hicma_desc; int MBrk = 1; int NBrk = 1; int Mrk = HICMA_descCUV->mt; int Nrk = HICMA_descCUV->mt; - aData.GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_CRK, is_OOC, nullptr, float_point, - MBrk, NBrk, MBrk * NBrk, Mrk, Nrk, 0, 0, Mrk, Nrk, p_grid, q_grid); - aData.GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_Z_COPY, is_OOC, nullptr, float_point, - lts, lts, lts * lts, N, 1, 0, 0, N, 1, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_CRK, is_OOC, nullptr, float_point, + MBrk, NBrk, MBrk * NBrk, Mrk, Nrk, 0, 0, Mrk, Nrk, p_grid, q_grid); + + aData->GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_Z, is_OOC, nullptr, float_point, + lts, lts, lts * lts, full_problem_size, 1, 0, 0, full_problem_size, 1, p_grid, q_grid); + + aData->GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_Z_COPY, is_OOC, nullptr, float_point, + lts, lts, lts * lts, full_problem_size, 1, 0, 0, full_problem_size, 1, p_grid, q_grid); + if (aConfigurations.GetIsNonGaussian()) { + aData->GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_PRODUCT, is_OOC, nullptr, + float_point, + lts, lts, lts * lts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); + aData->GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_SUM, is_OOC, nullptr, + float_point, + lts, lts, lts * lts, 1, 1, 0, 0, 1, 1, p_grid, q_grid); + } } template -T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, ExaGeoStatData &aData, +T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const Kernel &aKernel) { this->SetContext(aHardware.GetContext(aConfigurations.GetComputation())); - if (!aData.GetDescriptorData()->GetIsDescriptorInitiated()) { - this->InitiateDescriptors(aConfigurations, *aData.GetDescriptorData(), apMeasurementsMatrix); + if (!aData->GetDescriptorData()->GetIsDescriptorInitiated()) { + this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(),aKernel.GetP(), apMeasurementsMatrix); } // Create a Hicma sequence, if not initialized before through the same descriptors RUNTIME_request_t request_array[2] = {HICMA_REQUEST_INITIALIZER, HICMA_REQUEST_INITIALIZER}; - if (!aData.GetDescriptorData()->GetSequence()) { + if (!aData->GetDescriptorData()->GetSequence()) { HICMA_sequence_t *sequence; this->ExaGeoStatCreateSequence(&sequence); - aData.GetDescriptorData()->SetSequence(sequence); - aData.GetDescriptorData()->SetRequest(request_array); + aData->GetDescriptorData()->SetSequence(sequence); + aData->GetDescriptorData()->SetRequest(request_array); } - auto pSequence = (HICMA_sequence_t *) aData.GetDescriptorData()->GetSequence(); + auto pSequence = (HICMA_sequence_t *) aData->GetDescriptorData()->GetSequence(); //Initialization T loglik, logdet, test_time, variance, variance1 = 1, variance2 = 1, variance3, dot_product, dot_product1, dot_product2, dot_product3, dzcpy_time, time_facto, time_solve, logdet_calculate, matrix_gen_time; @@ -111,52 +125,54 @@ T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware & int N; int lts; int max_rank = aConfigurations.GetMaxRank(); - int iter_count = aData.GetMleIterations(); + int iter_count = aData->GetMleIterations(); auto kernel_name = aConfigurations.GetKernelName(); int num_params = aKernel.GetParametersNumbers(); int acc = aConfigurations.GetAccuracy(); if (iter_count == 0) { - this->SetModelingDescriptors(aData, aConfigurations); + this->SetModelingDescriptors(aData, aConfigurations, aKernel.GetP()); } - auto *HICMA_descCUV = aData.GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, - DescriptorName::DESCRIPTOR_CUV).hicma_desc; - auto *HICMA_descC = aData.GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, - DescriptorName::DESCRIPTOR_C).hicma_desc; - auto *HICMA_descCD = aData.GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, - DescriptorName::DESCRIPTOR_CD).hicma_desc; - auto *HICMA_descCrk = aData.GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, - DescriptorName::DESCRIPTOR_CRK).hicma_desc; - auto *HICMA_descZ = aData.GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z).hicma_desc; - auto *CHAM_descZ = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z).chameleon_desc; - auto *CHAM_descZcpy = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z_COPY).chameleon_desc; - auto *HICMA_descZcpy = aData.GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z_COPY).hicma_desc; - auto *HICMA_desc_det = aData.GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, - DescriptorName::DESCRIPTOR_DETERMINANT).hicma_desc; - auto *CHAM_desc_product = aData.GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_PRODUCT).chameleon_desc; - + auto *HICMA_descCUV = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, + DescriptorName::DESCRIPTOR_CUV).hicma_desc; + auto *HICMA_descC = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, + DescriptorName::DESCRIPTOR_C).hicma_desc; + auto *HICMA_descCD = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, + DescriptorName::DESCRIPTOR_CD).hicma_desc; + auto *HICMA_descCrk = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, + DescriptorName::DESCRIPTOR_CRK).hicma_desc; + auto *HICMA_descZ = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, + DescriptorName::DESCRIPTOR_Z).hicma_desc; + auto *CHAM_descZ = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_Z).chameleon_desc; + auto *CHAM_descZcpy = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_Z_COPY).chameleon_desc; + auto *HICMA_descZcpy = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, + DescriptorName::DESCRIPTOR_Z_COPY).hicma_desc; + auto *HICMA_desc_det = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, + DescriptorName::DESCRIPTOR_DETERMINANT).hicma_desc; + auto *CHAM_desc_product = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, + DescriptorName::DESCRIPTOR_PRODUCT).chameleon_desc; + auto *HICMA_desc_product = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, + DescriptorName::DESCRIPTOR_PRODUCT).chameleon_desc; + auto *HICMA_desc_sum = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, + DescriptorName::DESCRIPTOR_SUM).chameleon_desc; N = HICMA_descCUV->m; NRHS = HICMA_descZ->n; lts = HICMA_descZ->mb; - T *determinant = aData.GetDescriptorData()->GetDescriptorMatrix(HICMA_DESCRIPTOR, HICMA_desc_det); + T *determinant = aData->GetDescriptorData()->GetDescriptorMatrix(HICMA_DESCRIPTOR, HICMA_desc_det); *determinant = 0; - T *product = aData.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_product); + T *product = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_product); *product = 0; + T *sum; string recovery_file = aConfigurations.GetRecoveryFile(); if (recovery_file.empty() || !(this->recover((char *) (recovery_file.c_str()), iter_count, (T *) theta, &loglik, num_params))) { if (iter_count == 0) { - auto *z = new T[N]; - this->ExaGeoStatDesc2Lap(z, N, CHAM_descZ, EXAGEOSTAT_UPPER_LOWER); - this->ExaGeoStatLap2Desc(z, N, HICMA_descZ, EXAGEOSTAT_UPPER_LOWER); - delete[] z; + // Copy dense matrix Z into tile low rank Z. + this->CopyDescriptors(CHAM_descZ, HICMA_descZ, N, CHAMELEON_TO_HICMA); // Save a copy of descZ into descZcpy for restoring each iteration this->ExaGeoStatLapackCopyTile(EXAGEOSTAT_UPPER_LOWER, HICMA_descZ, HICMA_descZcpy); // Save another copy into descZcpy for chameleon, This is in case of other operations after Modeling. ex: Prediction. @@ -175,7 +191,14 @@ T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware & hicma_problem.kernel_type = aConfigurations.GetDistanceMetric() == common::GREAT_CIRCLE_DISTANCE ? STARSH_SPATIAL_MATERN2_GCD : STARSH_SPATIAL_MATERN2_SIMD; - HICMA_zgenerate_problem(HICMA_STARSH_PROB_GEOSTAT, 'S', 0, N, lts, HICMA_descCUV->mt, HICMA_descCUV->nt, + int hicma_data_type; + if (aConfigurations.GetIsNonGaussian()) { + hicma_data_type = HICMA_STARSH_PROB_GEOSTAT_NON_GAUSSIAN; + } else { + hicma_data_type = HICMA_STARSH_PROB_GEOSTAT; + } + + HICMA_zgenerate_problem(hicma_data_type, 'S', 0, N, lts, HICMA_descCUV->mt, HICMA_descCUV->nt, &hicma_problem); int compress_diag = 0; HICMA_zgytlr_Tile(EXAGEOSTAT_LOWER, HICMA_descCUV, HICMA_descCD, HICMA_descCrk, 0, max_rank, pow(10, -1.0 * acc), @@ -194,7 +217,8 @@ T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware & //Calculate Cholesky Factorization (C=LL-1) VERBOSE("LR: Cholesky factorization of Sigma...") START_TIMING(time_facto); - this->ExaGeoStatPotrfTile(EXAGEOSTAT_LOWER, HICMA_descCUV, 0, HICMA_descCD, HICMA_descCrk, max_rank, acc); + this->ExaGeoStatPotrfTile(EXAGEOSTAT_LOWER, HICMA_descCUV, 0, HICMA_descCD, HICMA_descCrk, max_rank, + pow(10, -1.0 * acc)); STOP_TIMING(time_facto); flops = flops + flops_dpotrf(N); @@ -210,6 +234,32 @@ T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware & STOP_TIMING(logdet_calculate); VERBOSE("Done.") + if (aConfigurations.GetIsNonGaussian()) { + VERBOSE("Transform Z vector to Gaussian field ...") + this->ExaGeoStatNonGaussianTransformTileAsync(HICMA_descZ, HICMA_desc_product, (T *) theta, pSequence, + &request_array[0]); + ExaGeoStatSequenceWait(pSequence); + + VERBOSE(" Done.") + sum = aData->GetDescriptorData()->GetDescriptorMatrix(HICMA_DESCRIPTOR, HICMA_desc_sum); + *sum = 0; + + VERBOSE("LR:Calculating dot product...") + CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_descZ, CHAM_descZ, 0, CHAM_desc_product); + VERBOSE(" Done.") + + double global_sum; +#if defined(CHAMELEON_USE_MPI) + MPI_Allreduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); +#else + global_sum = *product; +#endif + this->ExaGeoStatNonGaussianLogLikeTileAsync(HICMA_descZ, HICMA_desc_sum, (T *) theta, pSequence, + &request_array[0]); + ExaGeoStatSequenceWait(pSequence); + VERBOSE(" Done.") + } + //Solving Linear System (L*X=Z)--->inv(L)*Z VERBOSE("LR:Solving the linear system ...") START_TIMING(time_solve); @@ -220,10 +270,18 @@ T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware & flops = flops + flops_dtrsm(ChamLeft, N, NRHS); VERBOSE("Done.") + VERBOSE("Copy to chameleon") + this->CopyDescriptors(HICMA_descZ, CHAM_descZ, N, HICMA_TO_CHAMELEON); + VERBOSE("LR:Calculating dot product...") CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_descZ, CHAM_descZ, 0, CHAM_desc_product); dot_product = *product; - loglik = -0.5 * dot_product - 0.5 * logdet - (double) (N / 2.0) * log(2.0 * PI); + loglik = -0.5 * dot_product - 0.5 * logdet; + if (aConfigurations.GetIsNonGaussian()) { + loglik = loglik - *sum - N * log(theta[3]) - (double) (N / 2.0) * log(2.0 * PI); + } else { + loglik = loglik - (double) (N / 2.0) * log(2.0 * PI); + } VERBOSE("Done.") LOGGER(iter_count + 1 << " - Model Parameters (", true) @@ -263,7 +321,7 @@ T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware & LOGGER(" ---- Total Time: " << time_facto + logdet_calculate + time_solve) LOGGER(" ---- Gflop/s: " << flops / 1e9 / (time_facto + time_solve)) - aData.SetMleIterations(aData.GetMleIterations() + 1); + aData->SetMleIterations(aData->GetMleIterations() + 1); // for experiments and benchmarking accumulated_executed_time = @@ -379,17 +437,98 @@ int HicmaImplementation::ExaGeoStatMeasureDetTileAsync(void *apDescA, void *a return HICMA_SUCCESS; } +template +void *HicmaImplementation::ExaGeoStatDataGetAddr(void *apA, int aAm, int aAn) { + return HICMA_RUNTIME_data_getaddr((HICMA_desc_t *) apA, aAm, aAn); + +} template -void HicmaImplementation::ExaGeoStatLap2Desc(T *apA, const int &aLDA, void *apDescA, const UpperLower &aUpperLower) { - int status = HICMA_Lapack_to_Tile(apA, aLDA, (HICMA_desc_t *) apDescA); - if (status != HICMA_SUCCESS) { - throw std::runtime_error("HICMA_Lapack_to_Tile Failed!"); +int +HicmaImplementation::ExaGeoStatNonGaussianLogLikeTileAsync(void *apDescZ, void *apDescSum, const T *apTheta, + void *apSequence, + void *apRequest) { + // Check for initialize the Hicma context. + if (!this->mpContext) { + throw std::runtime_error( + "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); + } + HICMA_option_t options; + this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); + + int m, m0; + int tempmm; + auto Z = (HICMA_desc_t *) apDescZ; + + struct starpu_codelet *cl = &this->cl_non_gaussian_loglike_lr; + + + for (m = 0; m < Z->mt; m++) { + tempmm = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; + + m0 = m * Z->mb; + + starpu_insert_task(cl, + STARPU_VALUE, &tempmm, sizeof(int), + STARPU_VALUE, &m0, sizeof(int), + STARPU_R, ExaGeoStatDataGetAddr(apDescZ, m, 0), + STARPU_RW, ExaGeoStatDataGetAddr(apDescSum, 0, 0), + STARPU_VALUE, &apTheta[0], sizeof(double), + STARPU_VALUE, &apTheta[1], sizeof(double), + STARPU_VALUE, &apTheta[2], sizeof(double), + STARPU_VALUE, &apTheta[3], sizeof(double), + STARPU_VALUE, &apTheta[4], sizeof(double), + STARPU_VALUE, &apTheta[5], sizeof(double), +#if defined(CHAMELEON_CODELETS_HAVE_NAME) + STARPU_NAME, "non_gaussian_loglike_lr", +#endif + 0); } + + this->ExaGeoStatOptionsFree(&options); + this->ExaGeoStatOptionsFinalize(&options, (HICMA_context_t *) + this->mpContext); + return HICMA_SUCCESS; } + template -void *HicmaImplementation::ExaGeoStatDataGetAddr(void *apA, int aAm, int aAn) { - return HICMA_RUNTIME_data_getaddr((HICMA_desc_t *) apA, aAm, aAn); +int HicmaImplementation::ExaGeoStatNonGaussianTransformTileAsync(void *apDescZ, void *apDescFlag, const T *apTheta, + void *apSequence, void *apRequest) { + // Check for initialize the Hicma context. + if (!this->mpContext) { + throw std::runtime_error( + "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); + } + HICMA_option_t options; + this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); + + int m, m0; + int tempmm; + auto Z = (HICMA_desc_t *) apDescZ; + struct starpu_codelet *cl = &this->cl_non_gaussian_transform_lr; -} \ No newline at end of file + + for (m = 0; m < Z->mt; m++) { + tempmm = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; + m0 = m * Z->mb; + starpu_insert_task(cl, + STARPU_VALUE, &tempmm, sizeof(int), + STARPU_VALUE, &m0, sizeof(int), + STARPU_RW, ExaGeoStatDataGetAddr(apDescZ, m, 0), + STARPU_VALUE, &apTheta[0], sizeof(double), + STARPU_VALUE, &apTheta[1], sizeof(double), + STARPU_VALUE, &apTheta[2], sizeof(double), + STARPU_VALUE, &apTheta[3], sizeof(double), + STARPU_VALUE, &apTheta[4], sizeof(double), + STARPU_VALUE, &apTheta[5], sizeof(double), +#if defined(CHAMELEON_CODELETS_HAVE_NAME) + STARPU_NAME, "non_gaussian_transform_lr", +#endif + 0); + } + this->ExaGeoStatOptionsFree(&options); + this->ExaGeoStatOptionsFinalize(&options, (HICMA_context_t *) + this->mpContext); + return HICMA_SUCCESS; +} diff --git a/src/prediction/Prediction.cpp b/src/prediction/Prediction.cpp index 1550c5df..8454a66e 100644 --- a/src/prediction/Prediction.cpp +++ b/src/prediction/Prediction.cpp @@ -22,7 +22,8 @@ using namespace exageostat::configurations; using namespace exageostat::dataunits; template -void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHardware, ExaGeoStatData &aData, +void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, Configurations &aConfigurations, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) { @@ -35,13 +36,14 @@ void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHard break; } } - if (!can_predict && (aConfigurations.GetIsMLOEMMOM() || aConfigurations.GetIsMSPE() || aConfigurations.GetIsFisher())) { + if (!can_predict && + (aConfigurations.GetIsMLOEMMOM() || aConfigurations.GetIsMSPE() || aConfigurations.GetIsFisher())) { throw std::runtime_error( "Can't predict without an estimated theta, please either pass --etheta or run the modeling module before prediction"); } int number_of_mspe = 3; - int p = aConfigurations.GetP(); + int p = aKernel.GetP(); int z_miss_number = aConfigurations.GetUnknownObservationsNb(); int n_z_obs = aConfigurations.CalculateZObsNumber(); auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(common::EXACT_DENSE); @@ -49,20 +51,26 @@ void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHard //FISHER Prediction Function Call if (aConfigurations.GetIsFisher()) { LOGGER("---- Using Prediction Function Fisher ----") - auto fisher_results = new T[num_params * num_params]; - fisher_results = linear_algebra_solver->ExaGeoStatFisherTile(aConfigurations, aData, aHardware, (T *) aConfigurations.GetEstimatedTheta().data(), aKernel); + T *fisher_results; + fisher_results = linear_algebra_solver->ExaGeoStatFisherTile(aConfigurations, aData, aHardware, + (T *) aConfigurations.GetEstimatedTheta().data(), + aKernel); - LOGGER("- Sd of sigma2, alpha, nu: " << sqrt(fisher_results[0]) << " " << sqrt(fisher_results[4]) << " " << sqrt(fisher_results[8])) - LOGGER("- CI for sigma2: " << aConfigurations.GetEstimatedTheta()[0] - Q_NORM * sqrt(fisher_results[0]) << " " << aConfigurations.GetEstimatedTheta()[0] + Q_NORM * sqrt(fisher_results[0])) + LOGGER("- Sd of sigma2, alpha, nu: " << sqrt(fisher_results[0]) << " " << sqrt(fisher_results[4]) << " " + << sqrt(fisher_results[8])) + LOGGER("- CI for sigma2: " << aConfigurations.GetEstimatedTheta()[0] - Q_NORM * sqrt(fisher_results[0]) << " " + << aConfigurations.GetEstimatedTheta()[0] + Q_NORM * sqrt(fisher_results[0])) - LOGGER("- CI for alpha: " << aConfigurations.GetEstimatedTheta()[1] - Q_NORM * sqrt(fisher_results[4]) << " " << aConfigurations.GetEstimatedTheta()[1] + Q_NORM * sqrt(fisher_results[4])) - LOGGER("- CI for nu: " << aConfigurations.GetEstimatedTheta()[2] - Q_NORM * sqrt(fisher_results[8]) << " " << aConfigurations.GetEstimatedTheta()[2] + Q_NORM * sqrt(fisher_results[8])) + LOGGER("- CI for alpha: " << aConfigurations.GetEstimatedTheta()[1] - Q_NORM * sqrt(fisher_results[4]) << " " + << aConfigurations.GetEstimatedTheta()[1] + Q_NORM * sqrt(fisher_results[4])) + LOGGER("- CI for nu: " << aConfigurations.GetEstimatedTheta()[2] - Q_NORM * sqrt(fisher_results[8]) << " " + << aConfigurations.GetEstimatedTheta()[2] + Q_NORM * sqrt(fisher_results[8])) LOGGER("- Fisher Matrix:") - for (i = 0; i < num_params; i ++){ + for (i = 0; i < num_params; i++) { LOGGER(" ", true) - for (j = 0; j < num_params; j++){ - LOGGER_PRECISION(fisher_results[i * num_params + j] , 18) + for (j = 0; j < num_params; j++) { + LOGGER_PRECISION(fisher_results[i * num_params + j], 18) if (j != num_params - 1) { LOGGER_PRECISION(", ") } @@ -73,22 +81,26 @@ void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHard delete[] fisher_results; } - if (z_miss_number <= 0){ + if (z_miss_number <= 0) { return; } - LOGGER("- Number of Z observations: " << aConfigurations.GetP() * n_z_obs) - results::Results::GetInstance()->SetZMiss(aConfigurations.GetP() * n_z_obs); - T *z_obs = new T[p * n_z_obs]; - T *z_miss = new T[p * z_miss_number]; - T *z_actual = new T[p * z_miss_number]; + LOGGER("- Total number of Z: " << aConfigurations.GetProblemSize()) + LOGGER("- Number of Z Miss: " << z_miss_number) + LOGGER("- Number of Z observations: " << n_z_obs) + + results::Results::GetInstance()->SetZMiss(z_miss_number); + T *z_obs = new T[n_z_obs * p]; + T *z_miss = new T[z_miss_number]; + T *z_actual = new T[z_miss_number * p]; std::vector avg_pred_value(number_of_mspe); - auto miss_locations = new Locations(z_miss_number, aData.GetLocations()->GetDimension()); - auto obs_locations = new Locations(n_z_obs, aData.GetLocations()->GetDimension()); - // We Predict date with only Exact computation. This is a pre-request. + auto miss_locations = new Locations(z_miss_number, aConfigurations.GetDimension()); + // Prediction is only supported with 2D. + auto obs_locations = new Locations(n_z_obs, aConfigurations.GetDimension()); + // We Predict date with only Exact computation. This is a pre-request. InitializePredictionArguments(aConfigurations, aData, linear_algebra_solver, z_obs, z_actual, *miss_locations, - *obs_locations, apMeasurementsMatrix); + *obs_locations, apMeasurementsMatrix, p); // MLOE MMOM Auxiliary Function Call if (aConfigurations.GetIsMLOEMMOM()) { @@ -97,7 +109,6 @@ void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHard (T *) aConfigurations.GetInitialTheta().data(), (T *) aConfigurations.GetEstimatedTheta().data(), *miss_locations, *obs_locations, aKernel); - LOGGER(" ---- mloe_mmom Time(main): %6.2f seconds") } // IDW Auxiliary Function Call @@ -124,15 +135,29 @@ void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHard // MSPE Prediction Function Call if (aConfigurations.GetIsMSPE()) { LOGGER("---- Using MSPE ----") - T *prediction_error_mspe = linear_algebra_solver->ExaGeoStatMLEPredictTile(aData, - (T *) aConfigurations.GetEstimatedTheta().data(), - z_miss_number, n_z_obs, z_obs, - z_actual, z_miss, aHardware, - aConfigurations, *miss_locations, - *obs_locations, aKernel); + T *prediction_error_mspe; + if (aConfigurations.GetIsNonGaussian()) { + prediction_error_mspe = linear_algebra_solver->ExaGeoStatMLENonGaussianPredictTile(aData, + (T *) aConfigurations.GetEstimatedTheta().data(), + z_miss_number, n_z_obs, + z_obs, + z_actual, z_miss, + aHardware, + aConfigurations, + *miss_locations, + *obs_locations, aKernel); + } else { + prediction_error_mspe = linear_algebra_solver->ExaGeoStatMLEPredictTile(aData, + (T *) aConfigurations.GetEstimatedTheta().data(), + z_miss_number, n_z_obs, z_obs, + z_actual, z_miss, aHardware, + aConfigurations, *miss_locations, + *obs_locations, aKernel); + } for (i = 0; i < number_of_mspe; i++) { avg_pred_value[i] += prediction_error_mspe[i]; } + LOGGER("- MSPE: " << avg_pred_value[0]) LOGGER("- Average prediction Error (mspe): ", true) for (i = 0; i < number_of_mspe; i++) { LOGGER_PRECISION(avg_pred_value[i] << "\t", 8) @@ -149,15 +174,18 @@ void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHard } template -void Prediction::InitializePredictionArguments(Configurations &aConfigurations, ExaGeoStatData &aData, +void Prediction::InitializePredictionArguments(Configurations &aConfigurations, + std::unique_ptr> &aData, std::unique_ptr> &aLinearAlgebraSolver, T *apZObs, T *apZActual, Locations &aMissLocation, - Locations &aObsLocation, T *apMeasurementsMatrix) { + Locations &aObsLocation, T *apMeasurementsMatrix, const int &aP) { - int N = aConfigurations.GetProblemSize(); - T *z = new T[N]; + int full_problem_size = aConfigurations.GetProblemSize() * aP; + T *z = new T[full_problem_size]; - aLinearAlgebraSolver->ExaGeoStatGetZObs(aConfigurations, z, N, *aData.GetDescriptorData(), apMeasurementsMatrix); - PredictionHelpers::PickRandomPoints(aConfigurations, aData, apZObs, apZActual, z, aMissLocation, aObsLocation); + aLinearAlgebraSolver->ExaGeoStatGetZObs(aConfigurations, z, full_problem_size, *aData->GetDescriptorData(), + apMeasurementsMatrix, aP); + PredictionHelpers::PickRandomPoints(aConfigurations, aData, apZObs, apZActual, z, aMissLocation, aObsLocation, + aP); delete[] z; } \ No newline at end of file diff --git a/src/prediction/PredictionAuxiliaryFunctions.cpp b/src/prediction/PredictionAuxiliaryFunctions.cpp index fb89d847..4e5a3e23 100644 --- a/src/prediction/PredictionAuxiliaryFunctions.cpp +++ b/src/prediction/PredictionAuxiliaryFunctions.cpp @@ -57,8 +57,9 @@ void PredictionAuxiliaryFunctions::PredictIDW(T *apZMiss, T *apZActual, T *ap apMSPE[1] = error1 / (aZMissNumber / 2); apMSPE[2] = error2 / (aZMissNumber / 2); - LOGGER("- Z Actual .. Z Miss") - for (int index = 0; index < aZMissNumber; index++) - LOGGER(" (" << apZActual[index] << ", " << apZMiss[index] << ")") + VERBOSE("- Z Actual .. Z Miss") + for (int index = 0; index < aZMissNumber; index++) { + VERBOSE(" (" << apZActual[index] << ", " << apZMiss[index] << ")") + } LOGGER("- Prediction Error (IDW): " << apMSPE[0] << " - " << apMSPE[1] << " - " << apMSPE[2]) } \ No newline at end of file diff --git a/src/prediction/PredictionHelpers.cpp b/src/prediction/PredictionHelpers.cpp index 7acf4c3c..e9ecc9a1 100644 --- a/src/prediction/PredictionHelpers.cpp +++ b/src/prediction/PredictionHelpers.cpp @@ -21,23 +21,23 @@ using namespace exageostat::configurations; using namespace exageostat::dataunits; template -void PredictionHelpers::PickRandomPoints(Configurations &aConfigurations, ExaGeoStatData &aData, T *apZObs, +void PredictionHelpers::PickRandomPoints(Configurations &aConfigurations, + std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, Locations &aMissLocation, - Locations &aObsLocation) { + Locations &aObsLocation, const int &aP) { int i; int j; - int N = aConfigurations.GetProblemSize(); + int full_problem_size = aConfigurations.GetProblemSize() * aP; int z_miss_number = aConfigurations.GetUnknownObservationsNb(); int z_obs_number = aConfigurations.CalculateZObsNumber(); - auto l = new Locations(N, aData.GetLocations()->GetDimension()); - - int p = aConfigurations.GetP(); bool is_shuffle = true; + int p = aP; + auto l = new Locations(aConfigurations.GetProblemSize(), aData->GetLocations()->GetDimension()); - if (aConfigurations.GetIsMLOEMMOM() && aConfigurations.GetP() == 2) { + if (aConfigurations.GetIsMLOEMMOM() && aP == 2) { p = 1; - N /= 2; + full_problem_size /= 2; is_shuffle = false; } @@ -45,30 +45,35 @@ void PredictionHelpers::PickRandomPoints(Configurations &aConfigurations, Exa T **Z_parts = new T *[p]; // Allocate memory for each row of the 2D array for (i = 0; i < p; i++) { - Z_parts[i] = new T[N / p]; + Z_parts[i] = new T[full_problem_size / p]; } if (p > 1) { // Partition Z into p parts for (i = 0; i < p; i++) { - for (j = 0; j < N; j += p) { - Z_parts[i][j] = apZ[i + j]; + int m = 0; + for (j = 0; j < full_problem_size; j += p) { + Z_parts[i][m] = apZ[i + j]; + m++; } } } - for (i = 0; i < N / p; i++) { - l->GetLocationX()[i] = aData.GetLocations()->GetLocationX()[i]; - l->GetLocationY()[i] = aData.GetLocations()->GetLocationY()[i]; + for (i = 0; i < full_problem_size / p; i++) { + l->GetLocationX()[i] = aData->GetLocations()->GetLocationX()[i]; + l->GetLocationY()[i] = aData->GetLocations()->GetLocationY()[i]; + if (aConfigurations.GetDimension() != common::Dimension2D) { + l->GetLocationZ()[i] = aData->GetLocations()->GetLocationZ()[i]; + } } if (is_shuffle) { if (p == 1) { - Shuffle(apZ, *l, N); + Shuffle(apZ, *l, full_problem_size); } else if (p == 2) { - Shuffle(Z_parts[0], Z_parts[1], *l, N); + Shuffle(Z_parts[0], Z_parts[1], *l, full_problem_size / p); } else if (p == 3) { - Shuffle(Z_parts[0], Z_parts[1], Z_parts[2], *l, N); + Shuffle(Z_parts[0], Z_parts[1], Z_parts[2], *l, full_problem_size / p); } } @@ -107,11 +112,17 @@ void PredictionHelpers::PickRandomPoints(Configurations &aConfigurations, Exa for (i = 0; i < z_miss_number; i++) { aMissLocation.GetLocationX()[i] = l->GetLocationX()[i]; aMissLocation.GetLocationY()[i] = l->GetLocationY()[i]; + if (aConfigurations.GetDimension() != common::Dimension2D) { + aMissLocation.GetLocationZ()[i] = l->GetLocationZ()[i]; + } } for (i = 0; i < z_obs_number; i++) { aObsLocation.GetLocationX()[i] = l->GetLocationX()[z_miss_number + i]; aObsLocation.GetLocationY()[i] = l->GetLocationY()[z_miss_number + i]; + if (aConfigurations.GetDimension() != common::Dimension2D) { + aObsLocation.GetLocationZ()[i] = l->GetLocationZ()[z_miss_number + i]; + } } if (p == 1) { @@ -146,6 +157,12 @@ void PredictionHelpers::Shuffle(T *apArray, Locations &aLocations, int aSi aLocations.GetLocationY()[j] = aLocations.GetLocationY()[i]; aLocations.GetLocationY()[i] = y_temp; + if (aLocations.GetDimension() != common::Dimension2D) { + T z_temp = aLocations.GetLocationZ()[j]; + aLocations.GetLocationZ()[j] = aLocations.GetLocationZ()[i]; + aLocations.GetLocationZ()[i] = z_temp; + } + } } } @@ -174,6 +191,12 @@ void PredictionHelpers::Shuffle(T *apArray1, T *apArray2, Locations &aLoca aLocations.GetLocationY()[j] = aLocations.GetLocationY()[i]; aLocations.GetLocationY()[i] = y_temp; + if (aLocations.GetDimension() != common::Dimension2D) { + T z_temp = aLocations.GetLocationZ()[j]; + aLocations.GetLocationZ()[j] = aLocations.GetLocationZ()[i]; + aLocations.GetLocationZ()[i] = z_temp; + } + } } @@ -207,6 +230,11 @@ void PredictionHelpers::Shuffle(T *apArray1, T *apArray2, T *apArray3, Locati aLocations.GetLocationY()[j] = aLocations.GetLocationY()[i]; aLocations.GetLocationY()[i] = y_temp; + if (aLocations.GetDimension() != common::Dimension2D) { + T z_temp = aLocations.GetLocationZ()[j]; + aLocations.GetLocationZ()[j] = aLocations.GetLocationZ()[i]; + aLocations.GetLocationZ()[i] = z_temp; + } } } diff --git a/src/results/Results.cpp b/src/results/Results.cpp index 3a919d18..542283f3 100644 --- a/src/results/Results.cpp +++ b/src/results/Results.cpp @@ -48,80 +48,80 @@ void Results::PrintEndSummary() { LOGGER("") LOGGER("********************SUMMARY**********************") - auto locations_number = mGeneratedLocationsNumber; + auto locations_number = this->mGeneratedLocationsNumber; if (locations_number > 0) { LOGGER("---- Data Generation Results ----") - if (mIsSynthetic) { + if (this->mIsSynthetic) { LOGGER(" #Synthetic Dataset") } else { LOGGER(" #Real Dataset") } LOGGER(" #Number of Locations: " << locations_number) - if (mIsLogger && mIsSynthetic) { + if (this->mIsLogger && this->mIsSynthetic) { LOGGER(" #Data is written to file (", true) - if (mLoggerPath.empty()) { - mLoggerPath = LOG_PATH; + if (this->mLoggerPath.empty()) { + this->mLoggerPath = LOG_PATH; } - LOGGER_PRECISION(mLoggerPath << ").") + LOGGER_PRECISION(this->mLoggerPath << ").") LOGGER("") } - LOGGER(" #Total Data Generation Execution Time: " << mExecutionTimeDataGeneration) - LOGGER(" #Total Data Generation Gflop/s: " << mFlopsDataGeneration) + LOGGER(" #Total Data Generation Execution Time: " << this->mExecutionTimeDataGeneration) + LOGGER(" #Total Data Generation Gflop/s: " << this->mFlopsDataGeneration) LOGGER("") } - if (mMLEIterations > 0) { + if (this->mMLEIterations > 0) { LOGGER("---- Data Modeling Results ----") - LOGGER(" #Number of MLE Iterations till reach Maximum: " << mMLEIterations) + LOGGER(" #Number of MLE Iterations till reach Maximum: " << this->mMLEIterations) LOGGER(" #Found Maximum Theta at: ", true) - for (double i: mMaximumTheta) { + for (double i: this->mMaximumTheta) { LOGGER_PRECISION(i << " ", 8) } LOGGER("") - LOGGER(" #Final Log Likelihood value: " << mLogLikValue) + LOGGER(" #Final Log Likelihood value: " << this->mLogLikValue) LOGGER(" #Average Time Modeling per Iteration: " << this->GetAverageModelingExecutionTime()) LOGGER(" #Average Flops per Iteration: " << this->GetAverageModelingFlops()) - LOGGER(" #Total MLE Execution time: " << mTotalModelingExecutionTime) - LOGGER(" #Total MLE Gflop/s: " << mTotalModelingFlops) + LOGGER(" #Total MLE Execution time: " << this->mTotalModelingExecutionTime) + LOGGER(" #Total MLE Gflop/s: " << this->mTotalModelingFlops) LOGGER("") } - if (mZMiss > 0) { + if (this->mZMiss > 0) { LOGGER("---- Data Prediction Results ----") - LOGGER(" #Number of Missing Observations: " << mZMiss) - if (mMSPEError > 0) { + LOGGER(" #Number of Missing Observations: " << this->mZMiss) + if (this->mMSPEError > 0) { LOGGER(" #MSPE") - LOGGER(" #MSPE Prediction Execution Time: " << mExecutionTimeMSPE) - LOGGER(" #MSPE Gflop/s: " << mFlopsMSPE) - LOGGER(" #Mean Square Error MSPE: " << mMSPEError) + LOGGER(" #MSPE Prediction Execution Time: " << this->mExecutionTimeMSPE) + LOGGER(" #MSPE Gflop/s: " << this->mFlopsMSPE) + LOGGER(" #Mean Square Error MSPE: " << this->mMSPEError) } - if (!mIDWError.empty()) { + if (!this->mIDWError.empty()) { LOGGER(" #IDW") LOGGER(" #IDW Error: ( ", true) for (int i = 0; i < 3; i++) { - LOGGER_PRECISION(mIDWError[i] << " ", 8) + LOGGER_PRECISION(this->mIDWError[i] << " ", 8) } LOGGER_PRECISION(").") LOGGER("") } - if (mMLOE > 0 || mMMOM > 0) { + if (this->mMLOE > 0 || this->mMMOM > 0) { LOGGER(" #MLOE MMOM") - LOGGER(" #MLOE: " << mMLOE) - LOGGER(" #MMOM: " << mMMOM) - LOGGER(" #MLOE-MMOM Execution Time: " << mExecutionTimeMLOEMMOM) - LOGGER(" #MLOE-MMOM Matrix Generation Time: " << mGenerationTimeMLOEMMOM) - LOGGER(" #MLOE-MMOM Cholesky Factorization Time: " << mFactoTimeMLOEMMOM) - LOGGER(" #MLOE-MMOM Loop Time: " << mLoopTimeMLOEMMOM) - LOGGER(" #MLOE-MMOM Number of flops: " << mFlopsMLOEMMOM) + LOGGER(" #MLOE: " << this->mMLOE) + LOGGER(" #MMOM: " << this->mMMOM) + LOGGER(" #MLOE-MMOM Execution Time: " << this->mExecutionTimeMLOEMMOM) + LOGGER(" #MLOE-MMOM Matrix Generation Time: " << this->mGenerationTimeMLOEMMOM) + LOGGER(" #MLOE-MMOM Cholesky Factorization Time: " << this->mFactoTimeMLOEMMOM) + LOGGER(" #MLOE-MMOM Loop Time: " << this->mLoopTimeMLOEMMOM) + LOGGER(" #MLOE-MMOM Number of flops: " << this->mFlopsMLOEMMOM) LOGGER("") } } - if(mFisher00 != 0){ + if(this->mFisher00 != 0){ LOGGER(" #Fisher") - LOGGER(" #Sd For Sigma2: " << mFisher00) - LOGGER(" #Sd For Alpha: " << mFisher11) - LOGGER(" #Sd For Nu: " << mFisher22) - LOGGER(" #Fisher Execution Time: " << mTotalFisherTime) + LOGGER(" #Sd For Sigma2: " << this->mFisher00) + LOGGER(" #Sd For Alpha: " << this->mFisher11) + LOGGER(" #Sd For Nu: " << this->mFisher22) + LOGGER(" #Fisher Execution Time: " << this->mTotalFisherTime) LOGGER("") } LOGGER("*************************************************") diff --git a/tests/cpp-tests/api/TestExaGeoStatApi.cpp b/tests/cpp-tests/api/TestExaGeoStatApi.cpp index 1241baa5..6ce0b7ed 100644 --- a/tests/cpp-tests/api/TestExaGeoStatApi.cpp +++ b/tests/cpp-tests/api/TestExaGeoStatApi.cpp @@ -47,7 +47,7 @@ void TEST_GENERATE_DATA() { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(EXACT_DENSE, 4, 0); // Or you could use configurations.GetComputation(). - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); @@ -55,7 +55,7 @@ void TEST_GENERATE_DATA() { double expected_output_data[] = {-1.272336, -2.590700, 0.512143, -0.163880, 0.313504, -1.474411, 0.161705, 0.623389, -1.341858, -1.054282, -1.669383, 0.219171, 0.971214, 0.538973, -0.752828, 0.290822}; - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; double diff; @@ -108,8 +108,8 @@ void TEST_MODEL_DATA(Computation aComputation) { { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(aComputation, 4, 0); // Or you could use configurations.GetComputation(). - exageostat::dataunits::ExaGeoStatData data(configurations.GetProblemSize(), - configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); + //initiating the matrix of the CHAMELEON Descriptor Z. auto *z_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, @@ -133,8 +133,8 @@ void TEST_MODEL_DATA(Computation aComputation) { 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, 0.942824444953078489}; - data.GetLocations()->SetLocationX(*location_x, N); - data.GetLocations()->SetLocationY(*location_y, N); + data->GetLocations()->SetLocationX(*location_x, N); + data->GetLocations()->SetLocationY(*location_y, N); double log_likelihood = exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data, z_matrix); @@ -147,7 +147,7 @@ void TEST_MODEL_DATA(Computation aComputation) { { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(aComputation, 4, 0); // Or you could use configurations.GetComputation(). - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); double log_likelihood = exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, @@ -181,7 +181,7 @@ void TEST_PREDICTION() { Configurations::SetVerbosity(QUIET_MODE); auto hardware = ExaGeoStatHardware(EXACT_DENSE, configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); - exageostat::dataunits::ExaGeoStatData data(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); auto *z_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, @@ -203,8 +203,8 @@ void TEST_PREDICTION() { 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, 0.942824444953078489}; - data.GetLocations()->SetLocationX(*location_x, N); - data.GetLocations()->SetLocationY(*location_y, N); + data->GetLocations()->SetLocationX(*location_x, N); + data->GetLocations()->SetLocationY(*location_y, N); SECTION("Test Prediction - MSPE ONLY") { diff --git a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp index a26be8d0..29290c7d 100644 --- a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp @@ -47,7 +47,7 @@ void TEST_CSV_P_1() { vector initial_theta{1, 0.1, 0.5}; configurations.SetInitialTheta(initial_theta); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), @@ -88,14 +88,14 @@ void TEST_CSV_P_1() { locations.SetLocationY(*location_y, N); exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N, p, write_path, locations); - auto data = *csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, hardware, *pKernel); - auto z_desc_mat = data.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data.GetDescriptorData()->GetDescriptor( + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, + data->GetDescriptorData()->GetDescriptor( CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc); - auto data_loc_x = data.GetLocations()->GetLocationX(); - auto data_loc_y = data.GetLocations()->GetLocationY(); + auto data_loc_x = data->GetLocations()->GetLocationX(); + auto data_loc_y = data->GetLocations()->GetLocationY(); for (int i = 0; i < N; i++) { REQUIRE(z_desc_mat[i] - measurements_matrix[i] == Catch::Approx(0.0).margin(1e-6)); @@ -121,15 +121,15 @@ void TEST_CSV_P_1() { locations.SetLocationZ(*location_z, N); exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N, p, write_path, locations); - auto data = *csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, hardware, *pKernel); - auto z_desc_mat = data.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data.GetDescriptorData()->GetDescriptor( + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, + data->GetDescriptorData()->GetDescriptor( CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc); - auto data_loc_x = data.GetLocations()->GetLocationX(); - auto data_loc_y = data.GetLocations()->GetLocationY(); - auto data_loc_z = data.GetLocations()->GetLocationZ(); + auto data_loc_x = data->GetLocations()->GetLocationX(); + auto data_loc_y = data->GetLocations()->GetLocationY(); + auto data_loc_z = data->GetLocations()->GetLocationZ(); for (int i = 0; i < N; i++) { REQUIRE(z_desc_mat[i] - measurements_matrix[i] == Catch::Approx(0.0).margin(1e-6)); @@ -137,6 +137,7 @@ void TEST_CSV_P_1() { REQUIRE(data_loc_y[i] - location_y[i] == Catch::Approx(0.0).margin(1e-6)); REQUIRE(data_loc_z[i] - location_z[i] == Catch::Approx(0.0).margin(1e-6)); } + delete[] location_z; } SECTION("Test CSV ST Dimensions locations, p = 1.") { @@ -156,15 +157,15 @@ void TEST_CSV_P_1() { locations.SetLocationZ(*location_time, N); exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N, p, write_path, locations); - auto data = *csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, hardware, *pKernel); - auto z_desc_mat = data.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data.GetDescriptorData()->GetDescriptor( + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, + data->GetDescriptorData()->GetDescriptor( CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc); - auto data_loc_x = data.GetLocations()->GetLocationX(); - auto data_loc_y = data.GetLocations()->GetLocationY(); - auto data_loc_time = data.GetLocations()->GetLocationZ(); + auto data_loc_x = data->GetLocations()->GetLocationX(); + auto data_loc_y = data->GetLocations()->GetLocationY(); + auto data_loc_time = data->GetLocations()->GetLocationZ(); for (int i = 0; i < N; i++) { REQUIRE(z_desc_mat[i] - measurements_matrix[i] == Catch::Approx(0.0).margin(1e-6)); @@ -172,7 +173,7 @@ void TEST_CSV_P_1() { REQUIRE(data_loc_y[i] - location_y[i] == Catch::Approx(0.0).margin(1e-6)); REQUIRE(data_loc_time[i] - location_time[i] == Catch::Approx(0.0).margin(1e-6)); } - + delete[] location_time; } delete[] location_x; @@ -193,13 +194,12 @@ void TEST_CSV_P_2() { configurations.SetIsSynthetic(false); configurations.SetProblemSize(16); configurations.SetDenseTileSize(8); - int p = 2; - configurations.SetP(p); configurations.SetKernelName("BivariateMaternParsimonious"); configurations.SetComputation(exageostat::common::EXACT_DENSE); configurations.SetDataPath(read_path); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); + int p = pKernel->GetP(); auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), @@ -252,14 +252,14 @@ void TEST_CSV_P_2() { exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, locations); - auto data = *csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, hardware, *pKernel); - auto z_desc_mat = data.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data.GetDescriptorData()->GetDescriptor( + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, + data->GetDescriptorData()->GetDescriptor( CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc); - auto data_loc_x = data.GetLocations()->GetLocationX(); - auto data_loc_y = data.GetLocations()->GetLocationY(); + auto data_loc_x = data->GetLocations()->GetLocationX(); + auto data_loc_y = data->GetLocations()->GetLocationY(); for (int i = 0; i < N; i++) { REQUIRE(z_desc_mat[i] - measurements_matrix[i] == Catch::Approx(0.0).margin(1e-6)); @@ -289,15 +289,15 @@ void TEST_CSV_P_2() { exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, locations); - auto data = *csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, hardware, *pKernel); - auto z_desc_mat = data.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data.GetDescriptorData()->GetDescriptor( + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, + data->GetDescriptorData()->GetDescriptor( CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc); - auto data_loc_x = data.GetLocations()->GetLocationX(); - auto data_loc_y = data.GetLocations()->GetLocationY(); - auto data_loc_z = data.GetLocations()->GetLocationZ(); + auto data_loc_x = data->GetLocations()->GetLocationX(); + auto data_loc_y = data->GetLocations()->GetLocationY(); + auto data_loc_z = data->GetLocations()->GetLocationZ(); for (int i = 0; i < N; i++) { REQUIRE(z_desc_mat[i] - measurements_matrix[i] == Catch::Approx(0.0).margin(1e-6)); @@ -305,7 +305,7 @@ void TEST_CSV_P_2() { REQUIRE(data_loc_y[i] - location_y[i] == Catch::Approx(0.0).margin(1e-6)); REQUIRE(data_loc_z[i] - location_z[i] == Catch::Approx(0.0).margin(1e-6)); } - + delete[] location_z; } SECTION("Test CSV ST Dimensions locations, p = 2.") { @@ -329,15 +329,15 @@ void TEST_CSV_P_2() { exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, locations); - auto data = *csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, hardware, *pKernel); - auto z_desc_mat = data.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data.GetDescriptorData()->GetDescriptor( + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, + data->GetDescriptorData()->GetDescriptor( CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc); - auto data_loc_x = data.GetLocations()->GetLocationX(); - auto data_loc_y = data.GetLocations()->GetLocationY(); - auto data_loc_time = data.GetLocations()->GetLocationZ(); + auto data_loc_x = data->GetLocations()->GetLocationX(); + auto data_loc_y = data->GetLocations()->GetLocationY(); + auto data_loc_time = data->GetLocations()->GetLocationZ(); for (int i = 0; i < N; i++) { REQUIRE(z_desc_mat[i] - measurements_matrix[i] == Catch::Approx(0.0).margin(1e-6)); @@ -345,7 +345,7 @@ void TEST_CSV_P_2() { REQUIRE(data_loc_y[i] - location_y[i] == Catch::Approx(0.0).margin(1e-6)); REQUIRE(data_loc_time[i] - location_time[i] == Catch::Approx(0.0).margin(1e-6)); } - + delete[] location_time; } delete[] location_x; @@ -366,15 +366,14 @@ void TEST_CSV_P_3() { configurations.SetIsSynthetic(false); configurations.SetProblemSize(16); configurations.SetDenseTileSize(3); - int p = 3; - configurations.SetP(p); configurations.SetKernelName("TrivariateMaternParsimonious"); configurations.SetComputation(exageostat::common::EXACT_DENSE); configurations.SetDataPath(read_path); vector initial_theta{1, 1, 1, 0.1, 0.5, 1, 1.5, 0.1, 0.1, 0}; configurations.SetInitialTheta(initial_theta); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); + int p = pKernel->GetP(); auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), @@ -424,14 +423,14 @@ void TEST_CSV_P_3() { exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, locations); - auto data = *csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, hardware, *pKernel); - auto z_desc_mat = data.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data.GetDescriptorData()->GetDescriptor( + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, + data->GetDescriptorData()->GetDescriptor( CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc); - auto data_loc_x = data.GetLocations()->GetLocationX(); - auto data_loc_y = data.GetLocations()->GetLocationY(); + auto data_loc_x = data->GetLocations()->GetLocationX(); + auto data_loc_y = data->GetLocations()->GetLocationY(); for (int i = 0; i < N; i++) { REQUIRE(z_desc_mat[i] - measurements_matrix[i] == Catch::Approx(0.0).margin(1e-6)); @@ -459,15 +458,15 @@ void TEST_CSV_P_3() { exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, locations); - auto data = *csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, hardware, *pKernel); - auto z_desc_mat = data.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data.GetDescriptorData()->GetDescriptor( + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, + data->GetDescriptorData()->GetDescriptor( CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc); - auto data_loc_x = data.GetLocations()->GetLocationX(); - auto data_loc_y = data.GetLocations()->GetLocationY(); - auto data_loc_z = data.GetLocations()->GetLocationZ(); + auto data_loc_x = data->GetLocations()->GetLocationX(); + auto data_loc_y = data->GetLocations()->GetLocationY(); + auto data_loc_z = data->GetLocations()->GetLocationZ(); for (int i = 0; i < N; i++) { REQUIRE(z_desc_mat[i] - measurements_matrix[i] == Catch::Approx(0.0).margin(1e-6)); @@ -476,6 +475,7 @@ void TEST_CSV_P_3() { REQUIRE(data_loc_z[i] - location_z[i] == Catch::Approx(0.0).margin(1e-6)); } + delete[] location_z; } SECTION("Test CSV ST Dimensions locations, p = 3.") { @@ -496,15 +496,15 @@ void TEST_CSV_P_3() { exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, locations); - auto data = *csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, hardware, *pKernel); - auto z_desc_mat = data.GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data.GetDescriptorData()->GetDescriptor( + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, + data->GetDescriptorData()->GetDescriptor( CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc); - auto data_loc_x = data.GetLocations()->GetLocationX(); - auto data_loc_y = data.GetLocations()->GetLocationY(); - auto data_loc_time = data.GetLocations()->GetLocationZ(); + auto data_loc_x = data->GetLocations()->GetLocationX(); + auto data_loc_y = data->GetLocations()->GetLocationY(); + auto data_loc_time = data->GetLocations()->GetLocationZ(); for (int i = 0; i < N; i++) { REQUIRE(z_desc_mat[i] - measurements_matrix[i] == Catch::Approx(0.0).margin(1e-6)); @@ -512,7 +512,7 @@ void TEST_CSV_P_3() { REQUIRE(data_loc_y[i] - location_y[i] == Catch::Approx(0.0).margin(1e-6)); REQUIRE(data_loc_time[i] - location_time[i] == Catch::Approx(0.0).margin(1e-6)); } - + delete[] location_time; } delete[] location_x; diff --git a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp index 2c418bc7..c7b8dc67 100644 --- a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp @@ -191,7 +191,7 @@ void TEST_GENERATE_LOCATIONS() { synthetic_data_configurations.SetComputation(exageostat::common::EXACT_DENSE); vector initial_theta{1, 0.1, 0.5}; synthetic_data_configurations.SetInitialTheta(initial_theta); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(synthetic_data_configurations.GetKernelName()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(synthetic_data_configurations.GetKernelName(), synthetic_data_configurations.GetTimeSlot()); auto hardware = exageostat::hardware::ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), @@ -203,7 +203,7 @@ void TEST_GENERATE_LOCATIONS() { synthetic_data_configurations); synthetic_data_configurations.SetDimension(Dimension2D); - auto *data = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); + auto data = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); double *x = data->GetLocations()->GetLocationX(); double *y = data->GetLocations()->GetLocationY(); @@ -213,14 +213,13 @@ void TEST_GENERATE_LOCATIONS() { REQUIRE(x[i] != 0); REQUIRE(y[i] != 0); } - delete data; } SECTION("3D Generation") { synthetic_data_configurations.SetDimension(Dimension3D); unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( synthetic_data_configurations); - auto *data = synthetic_generator->CreateData(synthetic_data_configurations, + auto data = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); double *x = data->GetLocations()->GetLocationX(); @@ -232,7 +231,7 @@ void TEST_GENERATE_LOCATIONS() { REQUIRE(y[i] != 0); REQUIRE(z[i] != 0); } - delete data; + } SECTION("ST Generation") { @@ -240,7 +239,7 @@ void TEST_GENERATE_LOCATIONS() { synthetic_data_configurations.SetTimeSlot(2); unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( synthetic_data_configurations); - auto *data = synthetic_generator->CreateData(synthetic_data_configurations, + auto data = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); double *x = data->GetLocations()->GetLocationX(); @@ -252,7 +251,7 @@ void TEST_GENERATE_LOCATIONS() { REQUIRE(y[i] != 0.0); REQUIRE(z[i] != 0.0); } - delete data; + } delete pKernel; @@ -302,14 +301,14 @@ void TEST_GENERATION() { synthetic_data_configurations.GetCoresNumber(), synthetic_data_configurations.GetGPUsNumbers()); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(synthetic_data_configurations.GetKernelName()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(synthetic_data_configurations.GetKernelName(), synthetic_data_configurations.GetTimeSlot()); unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( synthetic_data_configurations); // Initialize the seed manually with zero, to get the first generated seeded numbers. int seed = 0; srand(seed); - auto *data = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); + auto data = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); // The expected output of the locations. vector x = {0.257389, 0.456062, 0.797269, 0.242161, 0.440742, 0.276432, 0.493965, 0.953933, 0.86952}; vector y = {0.138506, 0.238193, 0.170245, 0.579583, 0.514397, 0.752682, 0.867704, 0.610986, 0.891279}; @@ -320,7 +319,7 @@ void TEST_GENERATION() { } // Now test re-generating locations again, but without modifying seed manually which will results in completely new locations values - auto *data1 = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); + auto data1 = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); for (int i = 0; i < N; i++) { REQUIRE((data1->GetLocations()->GetLocationX()[i] - x[i]) != Catch::Approx(0.0).margin(1e-6)); REQUIRE((data1->GetLocations()->GetLocationY()[i] - y[i]) != Catch::Approx(0.0).margin(1e-6)); @@ -329,14 +328,11 @@ void TEST_GENERATION() { // Now if we modified seed again, we will get the first generated locations again. int seed_srand = 0; srand(seed_srand); - auto *data2 = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); + auto data2 = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); for (int i = 0; i < N; i++) { REQUIRE((data2->GetLocations()->GetLocationX()[i] - x[i]) == Catch::Approx(0.0).margin(1e-6)); REQUIRE((data2->GetLocations()->GetLocationY()[i] - y[i]) == Catch::Approx(0.0).margin(1e-6)); } - delete data; - delete data1; - delete data2; delete pKernel; } } diff --git a/tests/cpp-tests/kernels/CMakeLists.txt b/tests/cpp-tests/kernels/CMakeLists.txt index cc96f6b4..f38d9c23 100644 --- a/tests/cpp-tests/kernels/CMakeLists.txt +++ b/tests/cpp-tests/kernels/CMakeLists.txt @@ -11,7 +11,6 @@ set(EXAGEOSTAT_TESTFILES ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestUnivariateMaternStationary.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestUnivariateMaternNonStationary.cpp ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestBivariateMaternFlexible.cpp ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestUnivariateMaternDsigmaSquare.cpp ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestBivariateMaternParsimonious.cpp @@ -29,7 +28,6 @@ set(EXAGEOSTAT_TESTFILES ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestUnivariateMaternNonGaussian.cpp ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestUnivariateExpNonGaussian.cpp ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestTrivariateMaternParsimonious.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestUnivariateMaternNonStat.cpp ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE ) diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp index 9ad99895..7dae9ee4 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp @@ -49,10 +49,10 @@ void TEST_KERNEL_GENERATION_BivariateMaternFlexible() { int seed = 0; srand(seed); - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, + auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp index 497a979b..0feed29a 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp @@ -50,10 +50,10 @@ void TEST_KERNEL_GENERATION_BivariateMaternParsimonious() { int seed = 0; srand(seed); - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, + auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp index 81fde6bf..45c987bc 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp @@ -41,7 +41,7 @@ void TEST_KERNEL_GENERATION_BivariateSpacetimeMaternStationary() { synthetic_data_configurations.SetInitialTheta(initial_theta); synthetic_data_configurations.SetDimension(DimensionST); - synthetic_data_configurations.SetTimeSlot(5); + synthetic_data_configurations.SetTimeSlot(3); int dts = 4; synthetic_data_configurations.SetDenseTileSize(dts); synthetic_data_configurations.SetComputation(EXACT_DENSE); @@ -51,22 +51,19 @@ void TEST_KERNEL_GENERATION_BivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; + auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, + exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output - double expected_output_data[] = { - -1.272336, -2.516013, -1.182511, -2.512958, -1.203093, -2.548053, -1.213645, -2.511269, -1.807922, - -2.992990, -2.072972, -3.001448, -1.525724, -3.004085, -1.997874, -2.993439, -1.256771, -2.832258, - -1.022281, -2.827732, -0.924332, -2.856855, -1.363071, -2.832165, -1.680104, -3.400946, -1.685995, - -3.414854, -1.318928, -3.440336, -1.944915, -3.428945, -1.300296, -3.367150, -1.572847, -3.392844, - -1.126261, -3.392112, -1.682665, -3.394862 - }; - - for (size_t i = 0; i < N; i++) { + double expected_output_data[] = {-1.272336, -2.516013, -1.171584, -2.513616, -1.168821, -2.531118, -1.200293, + -1.960279, -1.919141, -2.005663, -2.279083, -2.006927, -0.807580, -1.719351, + -1.532754, -1.733719, -0.935139, -1.752957, 0.125841, -1.722780, -0.162095, + -1.738346, -0.866950, -1.753649}; + + for (size_t i = 0; i < N * 3 * 2; i++) { double diff = A[i] - expected_output_data[i]; REQUIRE(diff == Catch::Approx(0.0).margin(1e-6)); } diff --git a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp index 5c42c4c2..f8a678aa 100644 --- a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp @@ -48,10 +48,10 @@ void TEST_KERNEL_GENERATION_TrivariateMaternParsimonious() { int seed = 0; srand(seed); - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, + auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp index daec5161..7c6ba6cc 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp @@ -36,6 +36,12 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { synthetic_data_configurations.SetProblemSize(N); synthetic_data_configurations.SetKernelName("UnivariateMaternNonGaussian"); + vector lb{0.01, 0.01, -5, 0.1, -2}; + synthetic_data_configurations.SetLowerBounds(lb); + + vector ub{15, 5, 5, 5, 2, 2}; + synthetic_data_configurations.SetUpperBounds(ub); + vector initial_theta{7.0711, 1, 0, 2, 0, 0}; synthetic_data_configurations.SetInitialTheta(initial_theta); int dts = 3; @@ -46,10 +52,10 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { int seed = 0; srand(seed); - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, + auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output @@ -60,7 +66,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { }; for (size_t i = 0; i < N; i++) { - double diff = A[i] - expected_output_data[i]; + double diff = A[i] - expected_output_data[i]/2; REQUIRE(diff == Catch::Approx(0.0).margin(1e-6)); } } diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonStat.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonStat.cpp deleted file mode 100644 index a7382c04..00000000 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonStat.cpp +++ /dev/null @@ -1,75 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file TestUnivariateMaternNonStat.cpp - * @brief Unit tests for the TestUnivariateMaternNonStat kernel in the ExaGeoStat software package. - * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternNonStat kernel - * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @date 2023-05-10 -**/ - -#include -#include -#include - -using namespace std; - -using namespace exageostat::configurations; -using namespace exageostat::api; -using namespace exageostat::common; -using namespace exageostat::hardware; - -void TEST_KERNEL_GENERATION_UnivariateMaternNonStat() { - - SECTION("UnivariateMaternNonStat") - { - - // Create a new synthetic_data_configurations object with the provided command line arguments - Configurations synthetic_data_configurations; - - int N = 16; - synthetic_data_configurations.SetProblemSize(N); - synthetic_data_configurations.SetKernelName("UnivariateMaternNonStat"); - synthetic_data_configurations.SetDimension(Dimension2D); - - vector initial_theta{0.04, 1.57, 0.33, -1, 0.8, 0.1, -0.5, 0.5}; - synthetic_data_configurations.SetInitialTheta(initial_theta); - - int dts = 3; - synthetic_data_configurations.SetDenseTileSize(dts); - synthetic_data_configurations.SetComputation(EXACT_DENSE); - // initialize ExaGeoStat Hardware. - auto hardware = ExaGeoStatHardware(EXACT_DENSE, 3, 0); - - int seed = 0; - srand(seed); - exageostat::dataunits::ExaGeoStatData data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; - auto *A = (double *) CHAM_descriptorZ->mat; - // Define the expected output - double expected_output_data[] = { - -1.329875, -2.691617, 0.043584, -0.606902, -0.213657, -1.322967, - -0.281561, 0.084918, -1.152730, -1.209422, -1.776260, -0.410195, - 0.221300, 0.454534, -0.742289, 0.135949 - }; - - for (size_t i = 0; i < N; i++) { - double diff = A[i] - expected_output_data[i]; - REQUIRE(diff == Catch::Approx(0.0).margin(1e-6)); - } - } -} - -TEST_CASE("UnivariateMaternNonStat kernel test") { - TEST_KERNEL_GENERATION_UnivariateMaternNonStat(); - -} \ No newline at end of file diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonStationary.cpp deleted file mode 100644 index 35be6dc5..00000000 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonStationary.cpp +++ /dev/null @@ -1,13 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file TestUnivariateMaternNonStationary.cpp - * @brief - * @version 1.0.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @date 2023-05-08 -**/ diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp index da21bc33..5e94e762 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp @@ -48,10 +48,10 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNuggetsStationary() { int seed = 0; srand(seed); - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, + auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp index 461c0a20..6b2be137 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp @@ -49,11 +49,11 @@ void TEST_KERNEL_GENERATION_UnivariateMaternStationary() { int seed = 0; srand(seed); - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, + auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp index 365bcf7d..65bbc0f0 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp @@ -50,22 +50,18 @@ void TEST_KERNEL_GENERATION_UnivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; + auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, + exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output - double expected_output_data[] = { - -1.272336, -2.499643, 0.241533, -0.680865, - -0.966917, -2.919659, 0.289843, -0.387418, - -1.633527, -2.982286, -0.534392, -0.453970, - -0.907212, -2.455697, -0.617580, -0.289998, - -0.755687, -3.013465, 0.547427, -0.665510 - }; - - for (size_t i = 0; i < N; i++) { + double expected_output_data[] = {-1.272336, -2.600097, -0.482699, -0.533521, 0.008692, -1.750492, -0.453709, + 0.336176, -1.573801, -1.256633, -1.817694, -0.305688, 0.627641, 0.376389, + -0.939680, 0.167822, 0.514814, -1.315864, 1.884674, -0.234791}; + + for (size_t i = 0; i < N * 5; i++) { double diff = A[i] - expected_output_data[i]; REQUIRE(diff == Catch::Approx(0.0).margin(1e-6)); } diff --git a/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt b/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt index 555e0cb7..a48d6d5e 100644 --- a/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt +++ b/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt @@ -14,7 +14,7 @@ set(EXAGEOSTAT_TESTFILES ${EXAGEOSTAT_TESTFILES} ) -if (EXAGEOSTAT_USE_HICMA) +if (USE_HICMA) list(APPEND EXAGEOSTAT_TESTFILES ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestHiCMAImplementationTLR.cpp ) diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp index 4d889111..6de272d0 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp @@ -34,7 +34,7 @@ using namespace exageostat::hardware; void TEST_CHAMELEON_DESCRIPTORS_VALUES_DST() { Configurations synthetic_data_configurations; - + int p = 1; SECTION("SINGLE") { // initialize Hardware. @@ -47,7 +47,7 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES_DST() { synthetic_data_configurations.SetDenseTileSize(16); auto *data = new DescriptorData(); - linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, nullptr); + linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, p, nullptr); auto *CHAM_descriptorC = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; auto *CHAM_descriptorZ = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc; @@ -55,7 +55,7 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES_DST() { auto *CHAM_descriptorDeterminant = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_DETERMINANT).chameleon_desc; auto *CHAM_descriptorProduct = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT).chameleon_desc; - int N = synthetic_data_configurations.GetProblemSize() * synthetic_data_configurations.GetP(); + int N = synthetic_data_configurations.GetProblemSize(); int dts = synthetic_data_configurations.GetDenseTileSize(); int pGrid = synthetic_data_configurations.GetPGrid(); int qGrid = synthetic_data_configurations.GetQGrid(); @@ -171,7 +171,7 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES_DST() { synthetic_data_configurations.SetDenseTileSize(16); auto *data = new DescriptorData(); - linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, nullptr); + linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, p, nullptr); auto *CHAM_descriptorC = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; auto *CHAM_descriptorZ = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc; @@ -181,7 +181,7 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES_DST() { auto *CHAM_descriptorProduct = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT).chameleon_desc; auto *CHAM_descriptorProduct_1 = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT_1).chameleon_desc; auto *CHAM_descriptorProduct_2 = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT_2).chameleon_desc; - int N = synthetic_data_configurations.GetProblemSize() * synthetic_data_configurations.GetP(); + int N = synthetic_data_configurations.GetProblemSize(); int dts = synthetic_data_configurations.GetDenseTileSize(); int pGrid = synthetic_data_configurations.GetPGrid(); int qGrid = synthetic_data_configurations.GetQGrid(); diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp index c96b7417..a0a64517 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp @@ -33,7 +33,7 @@ using namespace exageostat::hardware; void TEST_CHAMELEON_DESCRIPTORS_VALUES() { Configurations synthetic_data_configurations; - + int p = 1; SECTION("SINGLE") { @@ -47,7 +47,7 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES() { synthetic_data_configurations.SetDenseTileSize(1); auto *data = new DescriptorData(); - linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, nullptr); + linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, p, nullptr); auto *CHAM_descriptorC = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; auto *CHAM_descriptorZ = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc; @@ -55,7 +55,7 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES() { auto *CHAM_descriptorDeterminant = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_DETERMINANT).chameleon_desc; auto *CHAM_descriptorProduct = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT).chameleon_desc; - int N = synthetic_data_configurations.GetProblemSize() * synthetic_data_configurations.GetP(); + int N = synthetic_data_configurations.GetProblemSize(); int dts = synthetic_data_configurations.GetDenseTileSize(); int pGrid = synthetic_data_configurations.GetPGrid(); int qGrid = synthetic_data_configurations.GetQGrid(); @@ -171,7 +171,7 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES() { synthetic_data_configurations.SetDenseTileSize(8); auto *data = new DescriptorData(); - linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, nullptr); + linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, p, nullptr); auto *CHAM_descriptorC = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; auto *CHAM_descsubC11 = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C11).chameleon_desc; @@ -187,7 +187,7 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES() { auto *CHAM_descriptorProduct_1 = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT_1).chameleon_desc; auto *CHAM_descriptorProduct_2 = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT_2).chameleon_desc; - int N = synthetic_data_configurations.GetProblemSize() * synthetic_data_configurations.GetP(); + int N = synthetic_data_configurations.GetProblemSize(); int dts = synthetic_data_configurations.GetDenseTileSize(); int pGrid = synthetic_data_configurations.GetPGrid(); int qGrid = synthetic_data_configurations.GetQGrid(); diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp index 00f1f51d..d5192e9b 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp @@ -52,14 +52,14 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { auto hardware = ExaGeoStatHardware(TILE_LOW_RANK, 1, 0); synthetic_data_configurations.SetApproximationMode(1); - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, synthetic_data_configurations, data); - auto *HICMA_descriptorC = data.GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_C).hicma_desc; + auto *HICMA_descriptorC = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_C).hicma_desc; int approximationMode = synthetic_data_configurations.GetApproximationMode(); - int N = synthetic_data_configurations.GetProblemSize() * synthetic_data_configurations.GetP(); + int N = synthetic_data_configurations.GetProblemSize(); int lts = synthetic_data_configurations.GetLowTileSize(); int pGrid = synthetic_data_configurations.GetPGrid(); int qGrid = synthetic_data_configurations.GetQGrid(); @@ -86,12 +86,12 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { // initialize Hardware. auto hardware = ExaGeoStatHardware(TILE_LOW_RANK, 1, 0); - exageostat::dataunits::ExaGeoStatData data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, synthetic_data_configurations, data); - int N = synthetic_data_configurations.GetProblemSize() * synthetic_data_configurations.GetP(); + int N = synthetic_data_configurations.GetProblemSize(); int lts = synthetic_data_configurations.GetLowTileSize(); int pGrid = synthetic_data_configurations.GetPGrid(); int qGrid = synthetic_data_configurations.GetQGrid(); @@ -101,15 +101,15 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { int maxRank = synthetic_data_configurations.GetMaxRank(); string actualObservationsFilePath = synthetic_data_configurations.GetActualObservationsFilePath(); - auto *HICMA_descriptorZ = data.GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_Z).hicma_desc; - auto *HICMA_descriptorZcpy = data.GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, + auto *HICMA_descriptorZ = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_Z).hicma_desc; + auto *HICMA_descriptorZcpy = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_Z_COPY).hicma_desc; - auto *HICMA_descriptorDeterminant = data.GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, + auto *HICMA_descriptorDeterminant = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_DETERMINANT).hicma_desc; - auto *HICMA_descriptorCD = data.GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_CD).hicma_desc; - auto *HICMA_descriptorCUV = data.GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, + auto *HICMA_descriptorCD = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_CD).hicma_desc; + auto *HICMA_descriptorCUV = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_CUV).hicma_desc; - auto *HICMA_descriptorCrk = data.GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, + auto *HICMA_descriptorCrk = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_CRK).hicma_desc; // Descriptor CD. diff --git a/tests/heavy-tests/CMakeLists.txt b/tests/heavy-tests/CMakeLists.txt new file mode 100644 index 00000000..b3feac02 --- /dev/null +++ b/tests/heavy-tests/CMakeLists.txt @@ -0,0 +1,14 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2023-12-20 + +enable_testing() +add_executable(exageostat-heavy-tests ${CMAKE_CURRENT_SOURCE_DIR}/HeavyTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ExamplesTests.cpp) +target_link_libraries(exageostat-heavy-tests Catch2::Catch2WithMain ${PROJECT_NAME}_INTERFACE) +catch_discover_tests(exageostat-heavy-tests) \ No newline at end of file diff --git a/tests/heavy-tests/ExamplesTests.cpp b/tests/heavy-tests/ExamplesTests.cpp new file mode 100644 index 00000000..df7133f1 --- /dev/null +++ b/tests/heavy-tests/ExamplesTests.cpp @@ -0,0 +1,97 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestExaGeoStatApi.cpp + * @brief Test suite for the ExaGeoStat APIs data generation functionality. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2023-12-26 +**/ + +#include +#include + +#include + +using namespace std; +using namespace std::filesystem; + +using namespace exageostat::configurations; +using namespace exageostat::dataunits; +using namespace exageostat::api; + +TEST_CASE("EXAMPLES") { + + // Specify the examples path + path currentPath = current_path().parent_path().parent_path() / "examples"; + + SECTION("Configuration"){ + path configurations_example = currentPath / "configurations/Example_Configurations "; + string arguments_string = "--N=10 --dts=8 --kernel=univariate_matern_stationary"; + cout << "Running Configurations example with arguments: " + arguments_string << endl << flush; + + std::string fullCommand = std::string(configurations_example) + " " + arguments_string; + if (std::system(fullCommand.c_str())) { + throw runtime_error("This test failed " + fullCommand); + } + } + SECTION("Synthetic Data Generation"){ + path synthetic_data_example = currentPath / "data-generators/Example_Synthetic_Data_Generation"; + string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --dimension=2d --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1"; + cout << "Running synthetic locations example with arguments: " + arguments_string << endl << flush; + + std::string fullCommand = std::string(synthetic_data_example) + " " + arguments_string; + if (std::system(fullCommand.c_str())) { + throw runtime_error("This test failed " + fullCommand); + } + } + currentPath = currentPath / "end-to-end"; + SECTION("Data Generation Module"){ + path data_generation_example = currentPath / "Example_Data_Generation"; + string arguments_string = "--N=32 --dts=8 --kernel=univariate_matern_stationary --dimension=3D --computation=diagonal_approx --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1"; + cout << "Running Data Generation Module example with arguments: " + arguments_string << endl << flush; + + std::string fullCommand = std::string(data_generation_example) + " " + arguments_string; + if (std::system(fullCommand.c_str())) { + throw runtime_error("This test failed " + fullCommand); + } + } + SECTION("Data Modeling Module"){ + path data_modeling_example = currentPath / "Example_Data_Modeling"; +#ifdef USE_HICMA + string arguments_string = "--N=16 --dts=8 --lts=8 --kernel=univariate_matern_stationary --dimension=2D --computation=tlr --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --max_mle_iterations=3 --tolerance=10 --max_rank=500"; +#else + string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --max_mle_iterations=3 --tolerance=10"; +#endif + cout << "Running Data Modeling example with arguments: " + arguments_string << endl << flush; + std::string fullCommand = std::string(data_modeling_example) + " " + arguments_string; + if (std::system(fullCommand.c_str())) { + throw runtime_error("This test failed " + fullCommand); + } + } + SECTION("Data Prediction Module"){ + path data_prediction_example = currentPath / "Example_Data_Prediction"; + + string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --Zmiss=10 --mspe --fisher --mloe_mmom --etheta=1.1:0.1:0.5"; + cout << "Running Data Prediction example with arguments: " + arguments_string << endl << flush; + + std::string fullCommand = std::string(data_prediction_example) + " " + arguments_string; + if (std::system(fullCommand.c_str())) { + throw runtime_error("This test failed " + fullCommand); + } + } + SECTION("Data Generation and Modeling"){ + path data_generation_modeling_example = currentPath / "Example_Data_Generation_and_Modeling"; + + string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --max_mle_iterations=2 --tolerance=4 --etheta=1.1:?:0.5"; + cout << "Running Data data generation and modeling example with arguments: " + arguments_string << endl << flush; + + std::string fullCommand = std::string(data_generation_modeling_example) + " " + arguments_string; + if (std::system(fullCommand.c_str())) { + throw runtime_error("This test failed " + fullCommand); + } + } +} diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp new file mode 100644 index 00000000..4b696252 --- /dev/null +++ b/tests/heavy-tests/HeavyTests.cpp @@ -0,0 +1,248 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestExaGeoStatApi.cpp + * @brief Test suite for the ExaGeoStat APIs data generation functionality. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2023-12-20 +**/ + +#include +#include +#include + +#include + +#include +#include + +using namespace std; + +using namespace exageostat::configurations; +using namespace exageostat::dataunits; +using namespace exageostat::api; +using namespace exageostat::kernels; + +void GenerateCommandLineArguments(const string &aKernelName, const string &aComputation, vector &aDimensions, + const string &aPrecision, vector &arguments_vector, + const string &aDistanceType) { + + // Search for the substring "TimeSpace" in the kernel name, As ST dimension only works with kernels having Spacetime in their name. + // This is a required convention described in the user manual + size_t found = aKernelName.find("Spacetime"); + // Check if the substring was found + if (found != std::string::npos) { + aDimensions = {"ST"}; + } else { + aDimensions = {"2D", "3D"}; + } + + // Set up a random number generator + random_device rd; + mt19937 gen(rd()); + int LOWER_SIZE = 5; + int MULTIPLICATION = 5; + int lts = INT16_MAX; + uniform_int_distribution problem_size_distribution(LOWER_SIZE, LOWER_SIZE * MULTIPLICATION); + int max_threads_size = static_cast(std::thread::hardware_concurrency()); + uniform_int_distribution cpu_size_distribution(1, max_threads_size - 1); + uniform_int_distribution max_iteration_distribution(1, 4); + uniform_int_distribution band_distribution(1, 6); + uniform_int_distribution tolerance_distribution(1, 5); + uniform_int_distribution max_rank_distribution(200, 600); + uniform_int_distribution seed_distribution(0, 3); + + int N_value = problem_size_distribution(gen); + + if (aComputation == "tlr") { + uniform_int_distribution lts_distribution(LOWER_SIZE, N_value); + lts = lts_distribution(gen); + N_value = lts * lts; + if (aKernelName.find("Bivariate") != string::npos || aKernelName.find("Trivariate") != string::npos) { + lts = 18; + } + arguments_vector.push_back("--lts=" + to_string(lts)); + } + + // tile size need to be smaller than N or lts in case of HiCMA. + uniform_int_distribution dts_distribution(LOWER_SIZE, min(N_value, lts)); + int dts = dts_distribution(gen); + + if (aKernelName.find("Bivariate") != string::npos || aKernelName.find("Trivariate") != string::npos) { + dts = 18; + N_value = 162; + } + if (aKernelName.find("Trivariate") != string::npos) { + N_value = 108; + } + arguments_vector.push_back("--dts=" + to_string(dts)); + arguments_vector.push_back("--N=" + to_string(N_value)); + + uniform_int_distribution zMiss_distribution(2, min(N_value / 2, 100)); + // Create a kernel object to get the number of parameters for each kernel. + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(aKernelName, 1); + int param_number = pKernel->GetParametersNumbers(); + delete pKernel; + + string initial_theta_arguments, lb_arguments, ub_arguments; + int zmiss; + zmiss = zMiss_distribution(gen); + if (aKernelName.find("Bivariate") != string::npos) { + do { + zmiss = zMiss_distribution(gen); + } while (zmiss % 2 != 0); + } + + if (aKernelName == "BivariateMaternFlexible") { + initial_theta_arguments = "0.3:0.6:0.01:0.3:0.9:0.9:0.05:0.3:1.5:0.9:0.99"; + lb_arguments = "0.01:0.01:0.01:0.01:0.01:0.01:0.01:0.01:0.01:0.01:0.01"; + ub_arguments = "50:50:50:50:50:50:50:50:50:50:50"; + } else if (aKernelName == "BivariateMaternParsimonious") { + initial_theta_arguments = "1:1:0.1:0.5:0.5:0.1"; + lb_arguments = "0.1:0.1:0.1:0.1:0.1:0.1"; + ub_arguments = "5:5:5:5:5:5"; + } else if (aKernelName == "BivariateSpacetimeMaternStationary" || aKernelName == "TrivariateMaternParsimonious") { + initial_theta_arguments = "1:1:0.1:0.5:0.5:0.1:0:0:0:0"; + lb_arguments = "0.1:0.1:0.1:0.1:0.1:0.1:0.1:0.1:0.1:0.1"; + ub_arguments = "5:5:5:5:5:5:5:5:5:5"; + } else { + uniform_real_distribution initial_theta_distribution(0.01, 2); + // Upper bond need to be larger than the initial theta + uniform_real_distribution ub_distribution(2, 5); + + for (int n = 0; n < param_number; n++) { + double initial_theta_value = initial_theta_distribution(gen); + initial_theta_arguments += to_string(initial_theta_value); + double lb_temp; + do { + lb_temp = initial_theta_distribution(gen); + } while (lb_temp >= initial_theta_value); + lb_arguments += to_string(lb_temp); + ub_arguments += to_string(ub_distribution(gen)); + if (n < param_number - 1) { + initial_theta_arguments += ":"; + ub_arguments += ":"; + lb_arguments += ":"; + } + } + } + + arguments_vector.push_back("--cores=" + to_string(cpu_size_distribution(gen))); + // TODO: Till fixing cuda error with multiple devices. +#ifdef USE_CUDA + arguments_vector.push_back("--gpus=" + "1"); +#endif + + arguments_vector.push_back("--kernel=" + aKernelName); + arguments_vector.push_back("--computation=" + aComputation); + arguments_vector.push_back("--precision=" + aPrecision); + arguments_vector.push_back("--initial_theta=" + initial_theta_arguments); + arguments_vector.push_back("--ub=" + ub_arguments); + arguments_vector.push_back("--lb=" + lb_arguments); + arguments_vector.push_back("--max_mle_iterations=" + to_string(max_iteration_distribution(gen))); + arguments_vector.push_back("--tolerance=" + to_string(tolerance_distribution(gen))); + arguments_vector.push_back("--max_rank=" + to_string(max_rank_distribution(gen))); + arguments_vector.push_back("--band=" + to_string(band_distribution(gen))); + arguments_vector.push_back("--ZMiss=" + to_string(zmiss)); + arguments_vector.push_back("--seed=" + to_string(seed_distribution(gen))); + arguments_vector.emplace_back("--idw"); + arguments_vector.emplace_back("--distance_metric=" + aDistanceType); + if (aKernelName.find("Bivariate") == string::npos && aKernelName.find("Trivariate") == string::npos) { + arguments_vector.emplace_back("--mloe-mmom"); + arguments_vector.emplace_back("--fisher"); + } + if (aKernelName.find("Trivariate") == string::npos) { + arguments_vector.emplace_back("--mspe"); + } +} + +TEST_CASE("END-TO-END") { + + // Add all the possible combination of code. + vector kernels_vector = { + "BivariateMaternFlexible", + "BivariateMaternParsimonious", + "BivariateSpacetimeMaternStationary", + "TrivariateMaternParsimonious", + "UnivariateExpNonGaussian", + "UnivariateMaternNonGaussian", + "UnivariateMaternNuggetsStationary", + "UnivariateMaternStationary", + "UnivariateSpacetimeMaternStationary" + }; + + vector computation_vector = {"exact", "diag_approx"}; +#ifdef USE_HICMA + computation_vector.emplace_back("tlr"); +#endif + vector dimensions_vector; + vector precision_vector = {"single", "double"}; + vector distance_vector = {"eg", "gcd"}; + + size_t combination_number = 1; + size_t number_of_iterations = 1; + for (int i = 0; i < number_of_iterations; i++) { + cout << "**** END-TO_END TESTS -- ITERATION NUMBER (" << i + 1 << ") ****" << endl; + // Generate combinations. + for (const auto ¤t_kernel: kernels_vector) { + for (const auto &computation: computation_vector) { + for (const auto &precision: precision_vector) { + for (const auto &distance_type: distance_vector) { + vector arguments_vector; + // This helper function will fill the arguments vector with the software arguments commands + GenerateCommandLineArguments(current_kernel, computation, dimensions_vector, precision, + arguments_vector, distance_type); + // Generate combination of dimensions. + for (const auto &dimension: dimensions_vector) { + // Add the dimension into the arguments vector + arguments_vector.push_back("--dimension=" + dimension); + + // Great Circle (GC) distance is only valid for 2D! + if (distance_type == "gcd" && (dimension == "3D" || dimension == "ST")) { + continue; + } + if (dimension == "ST" && computation == "tlr") { + continue; + } + if (dimension == "ST") { + random_device rd; + mt19937 gen(rd()); + uniform_int_distribution time_slot_distribution(1, 5); + arguments_vector.push_back("--time_slot=" + to_string(time_slot_distribution(gen))); + } + + string arguments_string; + for (const auto &j: arguments_vector) { + arguments_string += " " + j; + } + + cout << "(" << combination_number + << ") Testing the software with arguments: " + arguments_string << endl << flush; + + // Specify the executable path as the first argument + std::filesystem::path currentPath = + std::filesystem::current_path().parent_path().parent_path() / + "examples/end-to-end/Example_Data_Generation_Modeling_and_Prediction"; + std::string fullCommand = std::string(currentPath) + arguments_string; + if (std::system(fullCommand.c_str())) { + throw runtime_error("This test failed " + fullCommand); + } + + // To remove the previous dimension. + arguments_vector.pop_back(); + if (dimension == "ST") { + // To remove the time slot. + arguments_vector.pop_back(); + } + combination_number += 1; + } + } + } + } + } + } +} diff --git a/tests/heavy-tests/README.md b/tests/heavy-tests/README.md new file mode 100644 index 00000000..fe40887d --- /dev/null +++ b/tests/heavy-tests/README.md @@ -0,0 +1,3 @@ +# Heavy Tests Subdirectory + +This directory encompasses all the comprehensive tests for the project. It executes all project computations utilizing a range of technologies to guarantee the seamless functionality of every aspect. \ No newline at end of file From d8cefec85fd3baa1108009df207d10378c6febcb Mon Sep 17 00:00:00 2001 From: mahmoud Date: Wed, 10 Jan 2024 17:41:21 +0200 Subject: [PATCH 03/82] resolve threads plus some modifications --- CHANGELOG.md | 6 +- CMakeLists.txt | 6 +- ExaGeoStatCPPConfig.cmake.in | 4 +- Jenkinsfile | 6 +- USER_MANUAL.md | 84 ++++++++++--------- clean_build.sh | 2 +- cmake/{ImportBlas.cmake => ImportBLAS.cmake} | 4 +- ...{ImportBlasPP.cmake => ImportBLASPP.cmake} | 6 +- cmake/ImportCatch2.cmake | 2 +- cmake/ImportChameleon.cmake | 2 +- cmake/ImportGSL.cmake | 2 +- cmake/ImportHCore.cmake | 2 +- cmake/ImportHiCMA.cmake | 2 +- cmake/ImportHwloc.cmake | 2 +- cmake/ImportLapack.cmake | 2 +- cmake/ImportNLOPT.cmake | 2 +- cmake/ImportStarPu.cmake | 3 +- cmake/ImportStarsH.cmake | 2 +- cmake/macros/ImportDependency.cmake | 6 +- cmake/toolchains/CudaToolchain.cmake | 2 +- config.sh | 8 +- examples/configurations/CMakeLists.txt | 2 +- .../SyntheticDataGeneration.cpp | 2 +- examples/end-to-end/DataGeneration.cpp | 2 +- .../end-to-end/DataGenerationAndModeling.cpp | 2 +- .../DataGenerationModelingAndPrediction.cpp | 2 +- examples/end-to-end/DataModeling.cpp | 2 +- examples/end-to-end/DataPrediction.cpp | 2 +- inst/include/api/ExaGeoStat.hpp | 2 +- inst/include/common/Definitions.hpp | 2 +- inst/include/common/PluginRegistry.hpp | 2 +- .../include/configurations/Configurations.hpp | 2 +- .../include/data-generators/DataGenerator.hpp | 2 +- .../concrete/CSVDataGenerator.hpp | 2 +- .../concrete/SyntheticGenerator.hpp | 2 +- inst/include/data-units/DescriptorData.hpp | 2 +- .../data-units/ModelingDataHolders.hpp | 2 +- .../descriptor/ExaGeoStatDescriptor.hpp | 2 +- .../concrete/ChameleonDescriptor.hpp | 2 +- .../descriptor/concrete/HicmaDescriptor.hpp | 2 +- inst/include/hardware/ExaGeoStatHardware.hpp | 2 +- .../helpers/DistanceCalculationHelpers.hpp | 2 +- inst/include/kernels/Kernel.hpp | 18 ++-- .../LinearAlgebraMethods.hpp | 2 +- .../concrete/HicmaHeaders.hpp | 2 +- .../tile-low-rank/HicmaImplementation.hpp | 2 +- inst/include/prediction/Prediction.hpp | 2 +- inst/include/prediction/PredictionHelpers.hpp | 2 +- src/CMakeLists.txt | 2 +- src/api/ExaGeoStat.cpp | 2 +- src/configurations/Configurations.cpp | 2 +- .../concrete/CSVDataGenerator.cpp | 4 +- .../concrete/SyntheticGenerator.cpp | 4 +- src/data-units/DescriptorData.cpp | 2 +- .../descriptor/ExaGeoStatDescriptor.cpp | 2 +- .../descriptor/concrete/CMakeLists.txt | 2 +- .../concrete/ChameleonDescriptor.cpp | 2 +- .../descriptor/concrete/HicmaDescriptor.cpp | 2 +- src/hardware/ExaGeoStatHardware.cpp | 2 +- src/helpers/CommunicatorMPI.cpp | 2 +- src/helpers/DistanceCalculationHelpers.cpp | 2 +- src/kernels/CMakeLists.txt | 2 +- src/kernels/Kernel.cpp | 20 +---- .../concrete/BivariateMaternFlexible.cpp | 2 +- .../concrete/BivariateMaternParsimonious.cpp | 2 +- .../BivariateSpacetimeMaternStationary.cpp | 2 +- .../concrete/TrivariateMaternParsimonious.cpp | 2 +- .../concrete/UnivariateExpNonGaussian.cpp | 2 +- .../concrete/UnivariateMaternDbeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaBeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaNu.cpp | 2 +- .../concrete/UnivariateMaternDdnuNu.cpp | 2 +- .../UnivariateMaternDdsigmaSquare.cpp | 2 +- .../UnivariateMaternDdsigmaSquareBeta.cpp | 2 +- .../UnivariateMaternDdsigmaSquareNu.cpp | 2 +- src/kernels/concrete/UnivariateMaternDnu.cpp | 2 +- .../concrete/UnivariateMaternDsigmaSquare.cpp | 2 +- .../concrete/UnivariateMaternNonGaussian.cpp | 2 +- .../UnivariateMaternNuggetsStationary.cpp | 2 +- .../concrete/UnivariateMaternStationary.cpp | 2 +- .../UnivariateSpacetimeMaternStationary.cpp | 2 +- .../LinearAlgebraFactory.cpp | 2 +- .../LinearAlgebraMethods.cpp | 12 +-- .../concrete/CMakeLists.txt | 2 +- .../chameleon/ChameleonImplementation.cpp | 2 +- .../tile-low-rank/HicmaImplementation.cpp | 2 +- src/prediction/Prediction.cpp | 4 +- .../PredictionAuxiliaryFunctions.cpp | 2 +- src/prediction/PredictionHelpers.cpp | 2 +- src/results/Results.cpp | 2 +- tests/cpp-tests/api/TestExaGeoStatApi.cpp | 2 +- .../concrete/TestCSVDataGenerator.cpp | 6 +- .../concrete/TestSyntheticGenerator.cpp | 2 +- tests/cpp-tests/kernels/CMakeLists.txt | 2 +- .../concrete/TestBivariateMaternFlexible.cpp | 2 +- .../TestBivariateMaternParsimonious.cpp | 2 +- ...TestBivariateSpacetimeMaternStationary.cpp | 2 +- .../TestTrivariateMaternParsimonious.cpp | 2 +- .../TestUnivariateMaternNonGaussian.cpp | 2 +- .../TestUnivariateMaternNuggetsStationary.cpp | 2 +- .../TestUnivariateMaternStationary.cpp | 2 +- ...estUnivariateSpacetimeMaternStationary.cpp | 2 +- .../linear-algebra-solvers/CMakeLists.txt | 2 +- .../TestChameleonImplementationDST.cpp | 2 +- .../TestChameleonImplementationDense.cpp | 2 +- .../concrete/TestHiCMAImplementationTLR.cpp | 2 +- tests/heavy-tests/HeavyTests.cpp | 4 +- 107 files changed, 181 insertions(+), 202 deletions(-) rename cmake/{ImportBlas.cmake => ImportBLAS.cmake} (83%) rename cmake/{ImportBlasPP.cmake => ImportBLASPP.cmake} (85%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48461b8f..e99c5df8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,19 +14,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Automated the process of adding a new kernel. - Improved packaging of software with CPack. - Addressed installation issues. -- Fixed bivariate and trivariate kernel functionality. +- Fixed bivariate and trivariate kernels functionality. - Corrected time-space kernel issues. ### Changed - Updated the installation process for dependencies. - Modified the calculation of P for all kernels. - Adjusted CMake variables. -- Revised the process of finding BlasPP and Catch2 libraries. +- Revised the process of finding BLASPP and Catch2 libraries. - Updated doxygen documentation. ### Removed - Eliminated non-stationary kernel support. -- Removed Find OpenMP, LapackPP, and CuSolver. +- Removed Find OpenMP, LAPACKPP, and CuSOLVER. ## [1.0.0] - 2023-11-12 ### Added diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a43ab00..8f1b833d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ option(USE_MPI "Use MPI, if available" false) option(BUILD_TESTS "Option to enable building tests" OFF) option(BUILD_HEAVY_TESTS "Option to enable building heavy tests, This may take a lot of time" OFF) option(BUILD_EXAMPLES "Option to enable building examples" ON) -option(BUILD_DOCS "Build documentation in docs directory" OFF) +option(BUILD_DOCS "Build documentation in docs directory" ON) option(CREATE_PACKAGE "Enable a packaging system for distribution" OFF) # Cmake Module Paths @@ -125,9 +125,9 @@ endif () # ------------------------------- include(ImportChameleon) -# ExaGeoStatCPP depends on a LAPACK/BlasPP +# ExaGeoStatCPP depends on a LAPACK/BLASPP # ------------------------------- -include(ImportBlasPP) +include(ImportBLASPP) include(ImportLapack) # ExaGeoStatCPP Documentation diff --git a/ExaGeoStatCPPConfig.cmake.in b/ExaGeoStatCPPConfig.cmake.in index 89c24af2..ef8a1c2c 100644 --- a/ExaGeoStatCPPConfig.cmake.in +++ b/ExaGeoStatCPPConfig.cmake.in @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file ExaGeoStatCPPConfig.cmake.in -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @date 2023-01-30 @@ -117,7 +117,7 @@ include(ImportChameleon) # EXAGEOSTAT depends on a LAPACK/BLASPP # ------------------------------- -include(ImportBlasPP) +include(ImportBLASPP) include(ImportLapack) # Add all dependencies for ExaGeoStatCPP diff --git a/Jenkinsfile b/Jenkinsfile index 297d51c2..f38e9ab6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -25,7 +25,6 @@ pipeline { #################################################### module load mkl/2020.0.166 #################################################### - set -x ./config.sh -t -e ./clean_build.sh @@ -66,7 +65,6 @@ pipeline { #################################################### module load mkl/2020.0.166 #################################################### - set -x ./config.sh -t -e -H ./clean_build.sh @@ -88,7 +86,6 @@ pipeline { # BLAS/LAPACK #################################################### module load mkl/2020.0.166 - module load gsl/2.6-gcc-10.2.0 cd bin/ ctest --no-compress-output --verbose ''' @@ -105,8 +102,7 @@ pipeline { # BLAS/LAPACK #################################################### module load mkl/2020.0.166 - module load gsl/2.6-gcc-10.2.0 - ./config.sh -t -e + ./config.sh -e ./clean_build.sh cd bin make docs diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 1ca31c58..1cd0563d 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -3,45 +3,43 @@ ExaGeoStatCPP User Manual # Content -1. Configurations of the software. -2. Building ExaGeoStatCPP. -3. Supported Covariance kernels. -4. Arguments. -5. List of Descriptors. -6. Supported operations. -7. Contributing +1. [Configurations of the software](#configurations) +2. [Building ExaGeoStatCPP](#building) +3. [Supported Covariance kernels](#supported-covariance-functions-kernels-) +4. [Arguments](#arguments) +5. [List of Descriptors](#list-of-descriptors) +6. [Supported operations](#supported-operations) +7. [Contributing](#contributing) ## Configurations -* Run help of config.sh to know the needed arguments to run with your specific options. +* Run the help of `config.sh` to know the needed arguments for your specific options. + ```commandline ./config.sh -h ``` -* To Enable support of HiCMA add **```-H```** -* To enable examples add **```-e```** - -* To enable tests add **```-t```** - -* To enable CUDA add **```-c```** - -* To enable MPI add **```-m```** - -* To enable Verbose add **```-v```** -* To enable debug mode add **```-d```** +* To Enable support of HiCMA, add `-H`. +* To enable examples, add `-e`. +* To enable tests, add `-t`. +* To enable heavy tests, add `-T`. +* To enable CUDA, add `-c`. +* To enable MPI, add `-m`. +* To enable verbose output, add `-v`. +* To change the installation path of the dependencies, use `-i `. +* To enable manually passing mkl as BLA vendor, add `-s`. +* To enable packaging system for distribution, add `-p`. -* To change the installation path of the dependencies use **```-i ```** - -Please be aware that we currently offer support for either HiCMA or Chameleon, or both. ## Building -* Run help of clean_build.sh to know the additional arguments options. +* Run the help of `clean_build.sh` to know additional argument options. + ```commandline ./clean_build.sh -h ``` * Run clean_build.sh to clean, Build and Install the project. ```commandline -./clean_build.sh +./clean_build.sh -i ``` * To enable verbose printing, Run the following command. ```commandline @@ -67,14 +65,12 @@ Supported Covariance Functions/ Kernels: 10. univariate_matern_dnu 11. univariate_matern_dsigma_square 12. univariate_matern_non_gaussian -13. univariate_matern_non_sta -14. univariate_matern_non_stationary -15. univariate_matern_nuggets_stationary -16. univariate_spacetime_matern_stationary -17. bivariate_matern_flexible -18. bivariate_matern_parsimonious -19. bivariate_spacetime_matern_stationary -20. trivariate_matern_parsimonious +13. univariate_matern_nuggets_stationary +14. univariate_spacetime_matern_stationary +15. bivariate_matern_flexible +16. bivariate_matern_parsimonious +17. bivariate_spacetime_matern_stationary +18. trivariate_matern_parsimonious ## Arguments @@ -87,18 +83,18 @@ Supported Covariance Functions/ Kernels: * {Mandatory} To set the dense tile size in the case of Chameleon --dts= -* {Mandatory} To set the low tile size in case of HiCMA +* {Mandatory} To set the low tile size in case of HiCMA --lts= * {Optional} To set the dimension, the default is 2D --dimension=<2D/3D/ST> -* {Optional} To set the p grid +* {Optional} To set the p grid, the default is 1 - --p_grid= -* {Optional} To set the q grid + --p= +* {Optional} To set the q grid, the default is 1 - --q_grid= + --q= * {Optional} To set the time slot, the default is 1 --time_slot= @@ -107,7 +103,7 @@ Supported Covariance Functions/ Kernels: --computation= * {Optional} To set the precision, the default is double - --precision= + --precision= * {Optional} To set the number of cores, the default is 1 --cores= @@ -135,12 +131,15 @@ Supported Covariance Functions/ Kernels: * {Optional} To set the target theta --ttheta= +* {Optional} To set the estimated theta + + --etheta= * {Optional} To set the seed value, the default is 0 --seed= -* {Optional} To set the run mode value, the default is standard +* {Optional} To set the verbose value, the default is standard - --run_mode= + --verbose= * {Optional} To set the path of log files to be written, the default is ./exageostat-cpp/synthetic_ds/ --log_path= @@ -298,4 +297,7 @@ ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matri ```c++ // you have to pass your arguments through the configurations, your hardware and your data. ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); -``` \ No newline at end of file +``` + +## Contributing +[Contribution Guidelines](CONTRIBUTING.md) diff --git a/clean_build.sh b/clean_build.sh index 2802742f..d1f1a1d2 100755 --- a/clean_build.sh +++ b/clean_build.sh @@ -5,7 +5,7 @@ # @file clean_build.sh # @brief This script cleans and builds a software package called ExaGeoStat. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @date 2023-01-30 diff --git a/cmake/ImportBlas.cmake b/cmake/ImportBLAS.cmake similarity index 83% rename from cmake/ImportBlas.cmake rename to cmake/ImportBLAS.cmake index 2a488917..72e6d460 100644 --- a/cmake/ImportBlas.cmake +++ b/cmake/ImportBLAS.cmake @@ -2,7 +2,7 @@ # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). -# @file ImportBlas.cmake +# @file ImportBLAS.cmake # @brief This file searches for the BLAS library and includes it if not already included. # @version 1.0.1 # @author Mahmoud ElKarargy @@ -19,5 +19,5 @@ set(auto_gen OFF) set(url "https://github.com/xianyi/OpenBLAS") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") diff --git a/cmake/ImportBlasPP.cmake b/cmake/ImportBLASPP.cmake similarity index 85% rename from cmake/ImportBlasPP.cmake rename to cmake/ImportBLASPP.cmake index 53a099a6..ed61c977 100644 --- a/cmake/ImportBlasPP.cmake +++ b/cmake/ImportBLASPP.cmake @@ -2,13 +2,13 @@ # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). -# @file ImportBlasPP.cmake +# @file ImportBLASPP.cmake # @brief This file searches for the BLAS++ library and includes it if not already included. # @version 1.0.1 # @author Mahmoud ElKarargy # @date 2023-03-12 -include(ImportBlas) +include(ImportBLAS) #Configurations set(name blaspp) @@ -29,5 +29,5 @@ set(url "https://github.com/icl-utk-edu/blaspp") set(${name}_DIR "${CMAKE_INSTALL_PREFIX}/${capital_name}/lib/cmake/${name}") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") diff --git a/cmake/ImportCatch2.cmake b/cmake/ImportCatch2.cmake index 54531022..1d255902 100644 --- a/cmake/ImportCatch2.cmake +++ b/cmake/ImportCatch2.cmake @@ -20,5 +20,5 @@ set(auto_gen OFF) set(url "https://github.com/catchorg/Catch2.git") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") diff --git a/cmake/ImportChameleon.cmake b/cmake/ImportChameleon.cmake index c724fe4a..a71ae88c 100644 --- a/cmake/ImportChameleon.cmake +++ b/cmake/ImportChameleon.cmake @@ -21,7 +21,7 @@ set(auto_gen OFF) set(url "https://gitlab.inria.fr/solverstack/chameleon.git") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) include_directories(AFTER ${CHAMELEON_DIR_FOUND}/include/coreblas) include_directories(${CHAMELEON_DIR_FOUND}/chameleon-src) diff --git a/cmake/ImportGSL.cmake b/cmake/ImportGSL.cmake index b3aac862..f6ebd0ff 100644 --- a/cmake/ImportGSL.cmake +++ b/cmake/ImportGSL.cmake @@ -21,7 +21,7 @@ set(auto_gen OFF) set(url "https://ftp.gnu.org/gnu/gsl/gsl-2.7.1.tar.gz") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) # Add the GSL library to the project's list of libraries. list(APPEND LIBS gsl) diff --git a/cmake/ImportHCore.cmake b/cmake/ImportHCore.cmake index 5ec2a558..5f1c53cf 100644 --- a/cmake/ImportHCore.cmake +++ b/cmake/ImportHCore.cmake @@ -21,6 +21,6 @@ set(auto_gen OFF) set(url "https://github.com/ecrc/hcore.git") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") diff --git a/cmake/ImportHiCMA.cmake b/cmake/ImportHiCMA.cmake index 9efadc8e..7257ee15 100644 --- a/cmake/ImportHiCMA.cmake +++ b/cmake/ImportHiCMA.cmake @@ -21,7 +21,7 @@ set(auto_gen OFF) set(url "https://github.com/ecrc/hicma.git") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) # Include HiCMA headers in the project. include_directories(${HICMA_LIBDIR}/../hicma-src/hicma_ext) diff --git a/cmake/ImportHwloc.cmake b/cmake/ImportHwloc.cmake index 1804dd4c..a4f71525 100644 --- a/cmake/ImportHwloc.cmake +++ b/cmake/ImportHwloc.cmake @@ -21,6 +21,6 @@ set(auto_gen ON) set(url "https://github.com/open-mpi/hwloc") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") diff --git a/cmake/ImportLapack.cmake b/cmake/ImportLapack.cmake index 64e7a6a0..5a6a6c64 100644 --- a/cmake/ImportLapack.cmake +++ b/cmake/ImportLapack.cmake @@ -21,6 +21,6 @@ set(auto_gen OFF) set(url "https://github.com/xianyi/OpenBLAS") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") diff --git a/cmake/ImportNLOPT.cmake b/cmake/ImportNLOPT.cmake index 8c8feade..ac796b44 100644 --- a/cmake/ImportNLOPT.cmake +++ b/cmake/ImportNLOPT.cmake @@ -21,6 +21,6 @@ set(auto_gen OFF) set(url "https://github.com/stevengj/nlopt") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") diff --git a/cmake/ImportStarPu.cmake b/cmake/ImportStarPu.cmake index 84509c9a..86d661af 100644 --- a/cmake/ImportStarPu.cmake +++ b/cmake/ImportStarPu.cmake @@ -31,6 +31,7 @@ set(auto_gen ON) set(url "https://gitlab.inria.fr/starpu/starpu.git") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +message(" ${STARPU_COMPONENT_LIST}") +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${STARPU_COMPONENT_LIST} ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") diff --git a/cmake/ImportStarsH.cmake b/cmake/ImportStarsH.cmake index b74b4409..b7c842b6 100644 --- a/cmake/ImportStarsH.cmake +++ b/cmake/ImportStarsH.cmake @@ -21,6 +21,6 @@ set(auto_gen OFF) set(url "https://github.com/ecrc/stars-h.git") include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") diff --git a/cmake/macros/ImportDependency.cmake b/cmake/macros/ImportDependency.cmake index a7b2363e..c2de92f8 100644 --- a/cmake/macros/ImportDependency.cmake +++ b/cmake/macros/ImportDependency.cmake @@ -24,7 +24,7 @@ # If the package is found, it prints a message. If not, it calls the BuildDependency macro to fetch, # configure, build, and install the dependency. Finally, it attempts to find the package again to validate the installation. -macro(ImportDependency name tag version url flag is_cmake is_git auto_gen) +macro(ImportDependency name tag version url flag components is_cmake is_git auto_gen) # First, Check if no path is set for installation. if (CMAKE_INSTALL_PREFIX MATCHES "/usr/") @@ -44,7 +44,7 @@ macro(ImportDependency name tag version url flag is_cmake is_git auto_gen) IF (NOT TARGET ${name}) include(FindPkgConfig) find_package(PkgConfig QUIET) - find_package(${name} ${version} QUIET) + find_package(${name} ${version} QUIET COMPONENTS ${components}) # If the package is found, print a message if (${name}_FOUND) @@ -53,7 +53,7 @@ macro(ImportDependency name tag version url flag is_cmake is_git auto_gen) # If the package is not found, install it using BuildDependency message(" Can't find ${capital_name}, Installing it instead ..") BuildDependency(${name} ${url} ${tag} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) - find_package(${name} ${version} REQUIRED) + find_package(${name} ${version} REQUIRED COMPONENTS ${components}) endif () else () message(STATUS "${capital_name} already included") diff --git a/cmake/toolchains/CudaToolchain.cmake b/cmake/toolchains/CudaToolchain.cmake index 8486c427..aaf7ae59 100644 --- a/cmake/toolchains/CudaToolchain.cmake +++ b/cmake/toolchains/CudaToolchain.cmake @@ -5,7 +5,7 @@ # @file CudaToolchain.cmake # @brief This file is used to set up the CUDA toolchain for compilation. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-12 diff --git a/config.sh b/config.sh index 91028941..b947b2ef 100755 --- a/config.sh +++ b/config.sh @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file config.sh -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @date 2023-01-30 @@ -62,8 +62,8 @@ while getopts ":tevhHi:cmspT" opt; do echo -e "${GREEN}printing make with details.${NC}" VERBOSE=ON ;; - s) ##### Passing Blas vendor with mkl ##### - echo -e "${GREEN}MKL as a Blas vendor${NC}" + s) ##### Passing BLA vendor with mkl ##### + echo -e "${GREEN}MKL as a BLA vendor${NC}" BLAS_VENDOR="Intel10_64lp" ;; p) ##### Enabling packaging system for distribution ##### @@ -90,7 +90,7 @@ while getopts ":tevhHi:cmspT" opt; do printf "%20s %s\n" "-m :" "to enable using MPI." printf "%20s %s\n" "-v :" "to enable verbose printings." printf "%20s %s\n" "-d :" "to enable debug mode." - printf "%20s %s\n" "-s :" "to manually pass MKL as your blas vendor." + printf "%20s %s\n" "-s :" "to manually pass MKL as your bla vendor." printf "%20s %s\n" "-p :" "to enable a packaging system for distribution." printf "%20s %s\n" "-h :" "Help." echo "" diff --git a/examples/configurations/CMakeLists.txt b/examples/configurations/CMakeLists.txt index cfd13983..2c556898 100644 --- a/examples/configurations/CMakeLists.txt +++ b/examples/configurations/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief Defines an executable and links it with the ExaGeoStat library and other libraries. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @date 2023-01-31 diff --git a/examples/data-generators/SyntheticDataGeneration.cpp b/examples/data-generators/SyntheticDataGeneration.cpp index 9490524f..e9fa1465 100644 --- a/examples/data-generators/SyntheticDataGeneration.cpp +++ b/examples/data-generators/SyntheticDataGeneration.cpp @@ -6,7 +6,7 @@ /** * @file SyntheticLocationsGeneration.cpp * @brief This file contains the main function for generating synthetic Locations for ExaGeoStat - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-03-04 **/ diff --git a/examples/end-to-end/DataGeneration.cpp b/examples/end-to-end/DataGeneration.cpp index c497d9c1..cc454722 100644 --- a/examples/end-to-end/DataGeneration.cpp +++ b/examples/end-to-end/DataGeneration.cpp @@ -7,7 +7,7 @@ * @file DataGeneration.cpp * @brief This program either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data. * @details The program takes command line arguments to configure the data generation. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-05-30 **/ diff --git a/examples/end-to-end/DataGenerationAndModeling.cpp b/examples/end-to-end/DataGenerationAndModeling.cpp index 88c91aee..caf00a63 100644 --- a/examples/end-to-end/DataGenerationAndModeling.cpp +++ b/examples/end-to-end/DataGenerationAndModeling.cpp @@ -7,7 +7,7 @@ * @file DataGenerationAndModeling.cpp * @brief This program either generates synthetic data using the ExaGeoStat library, or reads an CSV file. * @details The program takes command line arguments to configure the data generation. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-06-21 **/ diff --git a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp index b9ade349..ef39d133 100644 --- a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp +++ b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp @@ -7,7 +7,7 @@ * @file DataGenerationModelingAndPrediction.cpp * @brief This program This program either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data, performs data modeling on loaded data, then predicts missing measurements using the ExaGeoStat library. * @details The program takes command line arguments to configure the data generation. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-06-21 **/ diff --git a/examples/end-to-end/DataModeling.cpp b/examples/end-to-end/DataModeling.cpp index 89832c55..6a4b3fb2 100644 --- a/examples/end-to-end/DataModeling.cpp +++ b/examples/end-to-end/DataModeling.cpp @@ -7,7 +7,7 @@ * @file DataModeling.cpp * @brief This program models data using the ExaGeoStat library. * @details The program takes command line arguments and example variables to configure the data modeling. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-06-21 **/ diff --git a/examples/end-to-end/DataPrediction.cpp b/examples/end-to-end/DataPrediction.cpp index f3bda1cd..2f681566 100644 --- a/examples/end-to-end/DataPrediction.cpp +++ b/examples/end-to-end/DataPrediction.cpp @@ -7,7 +7,7 @@ * @file DataPrediction.cpp * @brief This program predicts missing measurements using the ExaGeoStat library. * @details The program takes command line arguments to configure the data prediction module. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-09-11 **/ diff --git a/inst/include/api/ExaGeoStat.hpp b/inst/include/api/ExaGeoStat.hpp index a2aa5191..9a1aa9b7 100644 --- a/inst/include/api/ExaGeoStat.hpp +++ b/inst/include/api/ExaGeoStat.hpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStat.hpp * @brief High-Level Wrapper class containing the static API for ExaGeoStat operations. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-05-30 **/ diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index 184d21a4..935c78a7 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -5,7 +5,7 @@ /** * @file Definitions.hpp - * @version 1.0.0 + * @version 1.0.1 * @brief This file contains common definitions used in ExaGeoStat software package. * @details These definitions include enums for dimension, computation, precision, and floating point arithmetic; * A macro for instantiating template classes with supported types; and a set of available kernels. diff --git a/inst/include/common/PluginRegistry.hpp b/inst/include/common/PluginRegistry.hpp index 616909fc..23df532f 100644 --- a/inst/include/common/PluginRegistry.hpp +++ b/inst/include/common/PluginRegistry.hpp @@ -6,7 +6,7 @@ /** * @file PluginRegistry.hpp * @brief Defines a template class for registering and creating plugins. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-04-30 **/ diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index 85a26b3f..47968bc2 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -5,7 +5,7 @@ /** * @file Configurations.hpp -* @version 1.0.0 +* @version 1.0.1 * @brief Contains the declaration of the Configurations class and its member functions. * @author Sameh Abdulah * @author Mahmoud ElKarargy diff --git a/inst/include/data-generators/DataGenerator.hpp b/inst/include/data-generators/DataGenerator.hpp index 121a03ec..130065e3 100644 --- a/inst/include/data-generators/DataGenerator.hpp +++ b/inst/include/data-generators/DataGenerator.hpp @@ -6,7 +6,7 @@ /** * @file DataGenerator.hpp * @brief Contains definition for abstract Data Generator Class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-02-14 **/ diff --git a/inst/include/data-generators/concrete/CSVDataGenerator.hpp b/inst/include/data-generators/concrete/CSVDataGenerator.hpp index e75d6eea..cb5220b3 100644 --- a/inst/include/data-generators/concrete/CSVDataGenerator.hpp +++ b/inst/include/data-generators/concrete/CSVDataGenerator.hpp @@ -6,7 +6,7 @@ /** * @file CSVDataGenerator.hpp * @brief A class for generating synthetic data. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-14 diff --git a/inst/include/data-generators/concrete/SyntheticGenerator.hpp b/inst/include/data-generators/concrete/SyntheticGenerator.hpp index d08ed76e..e73e38be 100644 --- a/inst/include/data-generators/concrete/SyntheticGenerator.hpp +++ b/inst/include/data-generators/concrete/SyntheticGenerator.hpp @@ -6,7 +6,7 @@ /** * @file SyntheticGenerator.hpp * @brief A class for generating synthetic data. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-14 diff --git a/inst/include/data-units/DescriptorData.hpp b/inst/include/data-units/DescriptorData.hpp index 467daf72..d80e03b6 100644 --- a/inst/include/data-units/DescriptorData.hpp +++ b/inst/include/data-units/DescriptorData.hpp @@ -6,7 +6,7 @@ /** * @file DescriptorData.hpp * @brief Contains the definition of the DescriptorData class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-18 diff --git a/inst/include/data-units/ModelingDataHolders.hpp b/inst/include/data-units/ModelingDataHolders.hpp index d51c07ae..a6f932ea 100644 --- a/inst/include/data-units/ModelingDataHolders.hpp +++ b/inst/include/data-units/ModelingDataHolders.hpp @@ -1,7 +1,7 @@ /** * @file ModelingDataHolders.hpp * @brief This file contains the definition of the mModelingData struct, which contains all the data needed for modeling. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-08-24 **/ diff --git a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp index 17bcc39a..bfc85a6e 100644 --- a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp +++ b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStatDescriptor.hpp * @brief Class for creating matrix descriptors used in CHAMELEON and HiCMA libraries. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-16 diff --git a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp index a3c233c0..0f0f5560 100644 --- a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp @@ -6,7 +6,7 @@ /** * @file ChameleonDescriptor.hpp * @brief Defines the ChameleonDescriptor class for creating matrix descriptors using the CHAMELEON library. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp index e5ab7268..d3513252 100644 --- a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp @@ -6,7 +6,7 @@ /** * @file HicmaDescriptor.hpp * @brief Defines the Hicma Descriptor class for creating matrix descriptors using the HICMA library. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index 9d9e66db..8ed894a5 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStatHardware.hpp * @brief Contains the definition of the ExaGeoStatHardware class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-07 diff --git a/inst/include/helpers/DistanceCalculationHelpers.hpp b/inst/include/helpers/DistanceCalculationHelpers.hpp index 2d68eb7a..b297be6f 100644 --- a/inst/include/helpers/DistanceCalculationHelpers.hpp +++ b/inst/include/helpers/DistanceCalculationHelpers.hpp @@ -6,7 +6,7 @@ /** * @file DistanceCalculationHelpers.hpp * @brief Contains the definition of the DistanceCalculationHelpers class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/inst/include/kernels/Kernel.hpp b/inst/include/kernels/Kernel.hpp index 7bde9182..685b1d19 100644 --- a/inst/include/kernels/Kernel.hpp +++ b/inst/include/kernels/Kernel.hpp @@ -7,7 +7,7 @@ /** * @file Kernels.hpp * @brief Header file for the Kernels class, which contains the main kernel functions. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar @@ -100,14 +100,6 @@ namespace exageostat::kernels { dataunits::Locations &aLocation2, dataunits::Locations &aLocation3, T *apLocalTheta, const int &aDistanceMetric) = 0; - /** - * @brief Calculates the derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. - * @param[in] aOrder The order of the Bessel function. - * @param[in] aInputValue The input value at which to evaluate the derivative. - * @return The value of the derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. - */ - static T CalculateDerivativeBesselInputNu(const T &aOrder, const T &aInputValue); - /** * @brief Calculates the derivative of the modified Bessel function of the second kind (K_nu) with respect to its order, evaluated at input_value and order aOrder. * @param[in] aOrder The order of the Bessel function. @@ -137,10 +129,10 @@ namespace exageostat::kernels { /** * @brief Returns the value of the parameter P used by the kernel function. - * @return The value of P. + * @return The value of P (Variables Number). * */ - [[nodiscard]] int GetP() const; + [[nodiscard]] int GetVariablesNumber() const; /** * @brief Sets the value of the parameter P used by the kernel function. @@ -160,8 +152,8 @@ namespace exageostat::kernels { protected: //// Used P. int mP = 1; - //// Used P multiplied by timeslot - int mCalculatedP = 1; + //// Used Variable number which is P multiplied by timeslot + int mVariablesNumber = 1; //// Used number of parameters. int mParametersNumber = 3; }; diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp index 52b77aa6..679acf12 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp @@ -6,7 +6,7 @@ /** * @file LinearAlgebraMethods.hpp * @brief Header file for the LinearAlgebraMethods class, which defines the interface for linear algebra solvers. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 diff --git a/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp b/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp index 26a40ab4..9d216bbe 100644 --- a/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp +++ b/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp @@ -1,7 +1,7 @@ /** * @file HicmaHeaders.hpp * @brief This file contains the necessary includes for using the Chameleon library. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-08-24 **/ diff --git a/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp index e83f3970..22fdf9bf 100644 --- a/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp @@ -7,7 +7,7 @@ * @file HicmaImplementation.hpp * @brief This file contains the declaration of HicmaImplementation class. * @details HicmaImplementation is a concrete implementation of LinearAlgebraMethods class for tile low-rank matrices. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-26 diff --git a/inst/include/prediction/Prediction.hpp b/inst/include/prediction/Prediction.hpp index 31ba3453..e96742bc 100644 --- a/inst/include/prediction/Prediction.hpp +++ b/inst/include/prediction/Prediction.hpp @@ -6,7 +6,7 @@ /** * @file Prediction.hpp * @brief Contains the definition of the Prediction class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/inst/include/prediction/PredictionHelpers.hpp b/inst/include/prediction/PredictionHelpers.hpp index 1f84eda6..9a684390 100644 --- a/inst/include/prediction/PredictionHelpers.hpp +++ b/inst/include/prediction/PredictionHelpers.hpp @@ -6,7 +6,7 @@ /** * @file PredictionHelpers.hpp * @brief Contains the definition of the PredictionHelpers.hpp class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0cba4556..0fa6e7d0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief This file contains the CMake configuration for the ExaGeoStat library. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-01-30 diff --git a/src/api/ExaGeoStat.cpp b/src/api/ExaGeoStat.cpp index 396cbe41..d8a7d24a 100644 --- a/src/api/ExaGeoStat.cpp +++ b/src/api/ExaGeoStat.cpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStat.cpp * @brief High-Level Wrapper class containing the static API for ExaGeoStat operations. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-05-30 **/ diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index 3371a480..b15e5541 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -6,7 +6,7 @@ /** * @file Configurations.cpp * @brief This file defines the Configurations class which stores the configuration parameters for ExaGeoStat. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-01-31 diff --git a/src/data-generators/concrete/CSVDataGenerator.cpp b/src/data-generators/concrete/CSVDataGenerator.cpp index 65b27ccd..8d14d5a1 100644 --- a/src/data-generators/concrete/CSVDataGenerator.cpp +++ b/src/data-generators/concrete/CSVDataGenerator.cpp @@ -6,7 +6,7 @@ /** * @file CSVDataGenerator.cpp * @brief Implementation of the CSVDataGenerator class - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-14 @@ -48,7 +48,7 @@ CSVDataGenerator::CreateData(exageostat::configurations::Configurations &aCon vector z_locations; aKernel.SetPValue(aConfigurations.GetTimeSlot()); - int p = aKernel.GetP(); + int p = aKernel.GetVariablesNumber(); //Read the data out of the CSV file. ReadData(aConfigurations, measurements_vector, x_locations, y_locations, z_locations, p); diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index 0ac16e63..650e3a86 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -6,7 +6,7 @@ /** * @file SyntheticGenerator.cpp * @brief Implementation of the SyntheticGenerator class - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-14 @@ -76,7 +76,7 @@ SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aC std::string path = aConfigurations.GetLoggerPath(); DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), - aConfigurations.GetProblemSize(), aKernel.GetP(), path, + aConfigurations.GetProblemSize(), aKernel.GetVariablesNumber(), path, *data->GetLocations()); #endif VERBOSE("Done.") diff --git a/src/data-units/DescriptorData.cpp b/src/data-units/DescriptorData.cpp index 37ec11b6..c74ee931 100644 --- a/src/data-units/DescriptorData.cpp +++ b/src/data-units/DescriptorData.cpp @@ -6,7 +6,7 @@ /** * @file DescriptorData.cpp * @brief Contains the definition of the DescriptorData class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-18 diff --git a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp index aefb7edc..19eeb6ab 100644 --- a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp +++ b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStatDescriptor.cpp * @brief Implementation of creating matrix descriptors used in CHAMELEON and HiCMA libraries. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-17 diff --git a/src/data-units/descriptor/concrete/CMakeLists.txt b/src/data-units/descriptor/concrete/CMakeLists.txt index 9b863d6d..af483ee9 100644 --- a/src/data-units/descriptor/concrete/CMakeLists.txt +++ b/src/data-units/descriptor/concrete/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.0.1 # @brief CMake build script for the descriptors library, which includes the concrete implementations of the # Descriptors class based on the enabled libraries (HiCMA or Chameleon). # @author Mahmoud ElKarargy diff --git a/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp b/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp index 215407bc..bb62abc8 100644 --- a/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp +++ b/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file ChameleonDescriptor.cpp * @brief Defines the ChameleonDescriptor class for creating matrix descriptors using the CHAMELEON library. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp index dd4131f7..88650700 100644 --- a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp +++ b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file HicmaDescriptor.cpp * @brief Defines the Hicma Descriptor class for creating matrix descriptors using the HICMA library. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 21d489d9..f33a67b4 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStatHardware.cpp * @brief Contains the implementation of the ExaGeoStatHardware class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-07 diff --git a/src/helpers/CommunicatorMPI.cpp b/src/helpers/CommunicatorMPI.cpp index b63ee70e..e9167c4b 100644 --- a/src/helpers/CommunicatorMPI.cpp +++ b/src/helpers/CommunicatorMPI.cpp @@ -6,7 +6,7 @@ /** * @file CommunicatorMPI.cpp * @brief Defines the CommunicatorMPI class for MPI rank communication. - * @version 1.0.0 + * @version 1.0.1 * @author Sameh Abdulah * @date 2023-11-10 **/ diff --git a/src/helpers/DistanceCalculationHelpers.cpp b/src/helpers/DistanceCalculationHelpers.cpp index a5ff154d..3ee132cb 100644 --- a/src/helpers/DistanceCalculationHelpers.cpp +++ b/src/helpers/DistanceCalculationHelpers.cpp @@ -6,7 +6,7 @@ /** * @file DistanceCalculationHelpers.cpp * @brief Contains the implementation of the DistanceCalculationHelpers class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/src/kernels/CMakeLists.txt b/src/kernels/CMakeLists.txt index 645ac8ff..7a8a55da 100644 --- a/src/kernels/CMakeLists.txt +++ b/src/kernels/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief This file contains the CMake configuration for the kernels directory. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-04-11 diff --git a/src/kernels/Kernel.cpp b/src/kernels/Kernel.cpp index 464282a1..953cbb7c 100644 --- a/src/kernels/Kernel.cpp +++ b/src/kernels/Kernel.cpp @@ -6,7 +6,7 @@ /** * @file Kernel.cpp * @brief implementation file for the Kernels class, which contains the main kernel functions. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-12 @@ -23,18 +23,6 @@ using namespace std; using namespace exageostat::dataunits; using namespace exageostat::kernels; -template -T Kernel::CalculateDerivativeBesselInputNu(const T &aOrder, const T &aInputValue) { - if (aOrder < 1) { - T nu_new = abs(aOrder - 1); - return (-0.5 * (-CalculateDerivativeBesselNu(nu_new, aInputValue) + - CalculateDerivativeBesselNu(abs(aOrder + 1), aInputValue))); - } else { - return (-0.5 * (CalculateDerivativeBesselNu(aOrder - 1, aInputValue) + - CalculateDerivativeBesselNu(abs(aOrder + 1), aInputValue))); - } -} - template T Kernel::CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue) { if (aOrder == 0) { @@ -59,8 +47,8 @@ T Kernel::CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aI } template -int Kernel::GetP() const { - return this->mCalculatedP; +int Kernel::GetVariablesNumber() const { + return this->mVariablesNumber; } template @@ -69,7 +57,7 @@ void Kernel::SetPValue(int aTimeSlot) { // In case of uni-variate spacetime P = 1 * time slot // In case of Bi-variate spacetime P = 2 * time slot // P and timeslot will be constant with each created kernel, overriding the Calculated P value to handle case of calling the function multiple times. - this->mCalculatedP = this->mP * aTimeSlot; + this->mVariablesNumber = this->mP * aTimeSlot; } template diff --git a/src/kernels/concrete/BivariateMaternFlexible.cpp b/src/kernels/concrete/BivariateMaternFlexible.cpp index 6a12dd97..39a67c18 100644 --- a/src/kernels/concrete/BivariateMaternFlexible.cpp +++ b/src/kernels/concrete/BivariateMaternFlexible.cpp @@ -6,7 +6,7 @@ /** * @file BivariateMaternFlexible.cpp * @brief Implementation of the BivariateMaternFlexible kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/BivariateMaternParsimonious.cpp b/src/kernels/concrete/BivariateMaternParsimonious.cpp index 7faffc8c..223dbd3e 100644 --- a/src/kernels/concrete/BivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/BivariateMaternParsimonious.cpp @@ -6,7 +6,7 @@ /** * @file BivariateMaternParsimonious.cpp * @brief Implementation of the BivariateMaternParsimonious kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp index 5d26d9d7..08e65389 100644 --- a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp @@ -6,7 +6,7 @@ /** * @file BivariateSpacetimeMaternStationary.cpp * @brief Implementation of the BivariateSpacetimeMaternStationary kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/TrivariateMaternParsimonious.cpp b/src/kernels/concrete/TrivariateMaternParsimonious.cpp index ab0c0f5c..d5973678 100644 --- a/src/kernels/concrete/TrivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/TrivariateMaternParsimonious.cpp @@ -6,7 +6,7 @@ /** * @file TrivariateMaternParsimonious.cpp * @brief Implementation of the BivariateMaternParsimonious kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateExpNonGaussian.cpp b/src/kernels/concrete/UnivariateExpNonGaussian.cpp index cfe224e0..2007d78d 100644 --- a/src/kernels/concrete/UnivariateExpNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateExpNonGaussian.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateExpNonGaussian.cpp * @brief Implementation of the UnivariateExpNonGaussian kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDbeta.cpp b/src/kernels/concrete/UnivariateMaternDbeta.cpp index afe9921a..c0253e94 100644 --- a/src/kernels/concrete/UnivariateMaternDbeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDbeta.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDbeta.cpp * @brief Implementation of the UnivariateMaternDbeta kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp index b084ff2d..dec11267 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdbetaBeta.cpp * @brief Implementation of the UnivariateMaternDdbetaBeta kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp index 3fe34441..e202b96d 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdbetaNu.cpp * @brief Implementation of the UnivariateMaternDdbetaNu kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp index 45aa531c..7dec948b 100644 --- a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdnuNu.cpp * @brief Implementation of the UnivariateMaternDdnuNu kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp index 3e1a0468..ee980159 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquare.cpp * @brief Implementation of the UnivariateMaternDdsigmaSquare kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp index c3e43818..79c15bc4 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquareBeta.cpp * @brief Implementation of the UnivariateMaternDdsigmaSquareBeta kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp index 4861116b..19623e65 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquareNu.cpp * @brief Implementation of the UnivariateMaternDdsigmaSquareNu kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDnu.cpp b/src/kernels/concrete/UnivariateMaternDnu.cpp index c34b6e13..c62e47c1 100644 --- a/src/kernels/concrete/UnivariateMaternDnu.cpp +++ b/src/kernels/concrete/UnivariateMaternDnu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDnu.cpp * @brief Implementation of the UnivariateMaternDnu kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp index d4f86d09..2973c2af 100644 --- a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDsigmaSquare.cpp * @brief Implementation of the UnivariateMaternDsigmaSquare kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp index 0cd0ab0a..6d871568 100644 --- a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternNonGaussian.cpp * @brief Implementation of the UnivariateMaternNonGaussian kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp index 0ab266c4..7d899d7b 100644 --- a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternNuggetsStationary.cpp * @brief Implementation of the UnivariateMaternNuggetsStationary kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternStationary.cpp b/src/kernels/concrete/UnivariateMaternStationary.cpp index 0bdd8d06..93de1667 100644 --- a/src/kernels/concrete/UnivariateMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternStationary.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternStationary.cpp * @brief Implementation of the UnivariateMaternStationary kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp index 1ccb8add..19a99bce 100644 --- a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateSpacetimeMaternStationary.cpp * @brief Implementation of the UnivariateSpacetimeMaternStationary kernel. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp index ddfd312e..cfe6a88f 100644 --- a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp @@ -8,7 +8,7 @@ * @brief Implementation of the LinearAlgebraFactory class for creating linear algebra solvers for different computations using HiCMA or Chameleon libraries. * The factory creates a unique pointer to a concrete implementation of the LinearAlgebraMethods class based on the computation specified. * If the required library is not enabled, it throws a runtime_error exception. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-03-20 **/ diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index 6d6dc04e..76af27ff 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -6,7 +6,7 @@ /** * @file LinearAlgebraMethods.cpp * @brief Implementation of linear algebra methods. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 @@ -322,7 +322,7 @@ void LinearAlgebraMethods::GenerateSyntheticData(configurations::Configuratio const kernels::Kernel &aKernel) { this->mpContext = aHardware.GetChameleonContext(); - this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(), aKernel.GetP()); + this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(), aKernel.GetVariablesNumber()); auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); this->GenerateObservationsVector(aConfigurations, aData, aData->GetLocations(), aData->GetLocations(), &median_locations, aConfigurations.GetDistanceMetric(), aKernel); @@ -340,7 +340,7 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig throw std::runtime_error( "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); } - const int P = aKernel.GetP(); + const int P = aKernel.GetVariablesNumber(); const int full_problem_size = aConfigurations.GetProblemSize() * P; int seed = aConfigurations.GetSeed(); int initial_seed[4] = {seed, seed, seed, 1}; @@ -454,7 +454,7 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptrSetContext(aHardware.GetChameleonContext()); - this->InitiatePredictionDescriptors(aConfiguration, aData, aKernel.GetP()); + this->InitiatePredictionDescriptors(aConfiguration, aData, aKernel.GetVariablesNumber()); double time_solve, mat_gen_time, time_gemm, time_mspe = 0.0, flops = 0.0; int num_params; @@ -609,7 +609,7 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< int i; this->SetContext(aHardware.GetChameleonContext()); - this->InitiatePredictionDescriptors(aConfiguration, aData, aKernel.GetP()); + this->InitiatePredictionDescriptors(aConfiguration, aData, aKernel.GetVariablesNumber()); double time_solve, mat_gen_time, time_mse, mat_gen_time_2, dposv_time, gemms_time, time_trsm, time_gemm, time_mspe = 0.0, flops = 0.0; int num_params; @@ -807,7 +807,7 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu const kernels::Kernel &aKernel) { this->SetContext(aHardware.GetChameleonContext()); - this->InitiateMLOEMMOMDescriptors(aConfigurations, aData, aKernel.GetP()); + this->InitiateMLOEMMOMDescriptors(aConfigurations, aData, aKernel.GetVariablesNumber()); auto kernel_name = aConfigurations.GetKernelName(); auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); aData->CalculateMedianLocations(kernel_name, median_locations); diff --git a/src/linear-algebra-solvers/concrete/CMakeLists.txt b/src/linear-algebra-solvers/concrete/CMakeLists.txt index bc7b3e61..cc84ec89 100644 --- a/src/linear-algebra-solvers/concrete/CMakeLists.txt +++ b/src/linear-algebra-solvers/concrete/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.0.1 # @brief CMake build script for the linear-algebra-solvers library, which includes the concrete implementations of the # LinearAlgebraMethods class based on the enabled libraries (HiCMA or Chameleon). # @author Mahmoud ElKarargy diff --git a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp index 04b065ca..34e7af92 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp @@ -150,7 +150,7 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa this->SetContext(aHardware.GetContext(aConfigurations.GetComputation())); if (!aData->GetDescriptorData()->GetIsDescriptorInitiated()) { - this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(), aKernel.GetP(), apMeasurementsMatrix); + this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(), aKernel.GetVariablesNumber(), apMeasurementsMatrix); } // Create a Chameleon sequence, if not initialized before through the same descriptors RUNTIME_request_t request_array[2] = {RUNTIME_REQUEST_INITIALIZER, RUNTIME_REQUEST_INITIALIZER}; diff --git a/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp index 30608e74..037d2cfd 100644 --- a/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp @@ -6,7 +6,7 @@ /** * @file HicmaImplementation.cpp * @brief Sets up the HiCMA descriptors needed for the tile low rank computations in ExaGeoStat. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-26 diff --git a/src/prediction/Prediction.cpp b/src/prediction/Prediction.cpp index 8454a66e..dbb9817d 100644 --- a/src/prediction/Prediction.cpp +++ b/src/prediction/Prediction.cpp @@ -6,7 +6,7 @@ /** * @file Prediction.cpp * @brief Contains the implementation of the Prediction class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 @@ -43,7 +43,7 @@ void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHard } int number_of_mspe = 3; - int p = aKernel.GetP(); + int p = aKernel.GetVariablesNumber(); int z_miss_number = aConfigurations.GetUnknownObservationsNb(); int n_z_obs = aConfigurations.CalculateZObsNumber(); auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(common::EXACT_DENSE); diff --git a/src/prediction/PredictionAuxiliaryFunctions.cpp b/src/prediction/PredictionAuxiliaryFunctions.cpp index 4e5a3e23..1dd7b33b 100644 --- a/src/prediction/PredictionAuxiliaryFunctions.cpp +++ b/src/prediction/PredictionAuxiliaryFunctions.cpp @@ -6,7 +6,7 @@ /** * @file PredictionAuxiliaryFunctions.cpp * @brief Contains the implementation of the PredictionAuxiliaryFunctions class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/src/prediction/PredictionHelpers.cpp b/src/prediction/PredictionHelpers.cpp index e9ecc9a1..90da508b 100644 --- a/src/prediction/PredictionHelpers.cpp +++ b/src/prediction/PredictionHelpers.cpp @@ -6,7 +6,7 @@ /** * @file PredictionHelpers.cpp * @brief Contains the implementation of the PredictionHelpers class. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/src/results/Results.cpp b/src/results/Results.cpp index 542283f3..46805935 100644 --- a/src/results/Results.cpp +++ b/src/results/Results.cpp @@ -6,7 +6,7 @@ /** * @file Results.cpp * @brief Defines the Results class for storing and accessing result data. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-09-14 **/ diff --git a/tests/cpp-tests/api/TestExaGeoStatApi.cpp b/tests/cpp-tests/api/TestExaGeoStatApi.cpp index 6ce0b7ed..4074803f 100644 --- a/tests/cpp-tests/api/TestExaGeoStatApi.cpp +++ b/tests/cpp-tests/api/TestExaGeoStatApi.cpp @@ -6,7 +6,7 @@ /** * @file TestExaGeoStatApi.cpp * @brief Test suite for the ExaGeoStat APIs data generation functionality. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-07 diff --git a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp index 29290c7d..83667823 100644 --- a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp @@ -9,7 +9,7 @@ * @details This file contains Catch2 unit tests that validate the functionality of the CSVDataGenerator class * in the ExaGeoStat software package. The tests cover various aspects of data generation, including spreading * and reversing bits, generating locations for different dimensions, and testing helper functions. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-03-08 **/ @@ -199,7 +199,7 @@ void TEST_CSV_P_2() { configurations.SetDataPath(read_path); Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); - int p = pKernel->GetP(); + int p = pKernel->GetVariablesNumber(); auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), @@ -373,7 +373,7 @@ void TEST_CSV_P_3() { configurations.SetInitialTheta(initial_theta); Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); - int p = pKernel->GetP(); + int p = pKernel->GetVariablesNumber(); auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), diff --git a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp index c7b8dc67..3ff0971f 100644 --- a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp @@ -9,7 +9,7 @@ * @details This file contains Catch2 unit tests that validate the functionality of the SyntheticGenerator class * in the ExaGeoStat software package. The tests cover various aspects of data generation, including spreading * and reversing bits, generating locations for different dimensions, and testing helper functions. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-03-08 **/ diff --git a/tests/cpp-tests/kernels/CMakeLists.txt b/tests/cpp-tests/kernels/CMakeLists.txt index f38d9c23..01d039c3 100644 --- a/tests/cpp-tests/kernels/CMakeLists.txt +++ b/tests/cpp-tests/kernels/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @date 2023-04-29 diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp index 7dae9ee4..48cee8bb 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp @@ -7,7 +7,7 @@ * @brief Unit tests for the BivariateMaternFlexible kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the BivariateMaternFlexible kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-09 diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp index 0feed29a..cda682fe 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestBivariateMaternParsimonious kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestBivariateMaternParsimonious kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp index 45c987bc..6b0d4387 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestBivariateSpacetimeMaternStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestBivariateSpacetimeMaternStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 diff --git a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp index f8a678aa..4859dceb 100644 --- a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestTrivariateMaternParsimonious kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestTrivariateMaternParsimonious kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp index 7c6ba6cc..31fbd107 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternNonGaussian kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternNonGaussian kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp index 5e94e762..1f43a7fd 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternNuggetsStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternNuggetsStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp index 6b2be137..a9b4777a 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-29 diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp index 65bbc0f0..65147642 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateSpacetimeMaternStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateSpacetimeMaternStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 diff --git a/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt b/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt index a48d6d5e..f7afad0d 100644 --- a/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt +++ b/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @date 2023-04-06 diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp index 6de272d0..8c956b1b 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp @@ -6,7 +6,7 @@ /** * @file TestChameleonImplementationDST.cpp * @brief Unit tests for the Diagonal Super Tile computation in the ExaGeoStat software package. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-04-09 **/ diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp index a0a64517..a7d2c204 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp @@ -6,7 +6,7 @@ /** * @file TestChameleonImplmentationDense.cpp * @brief Unit tests for the Dense computation in the ExaGeoStat software package. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-04-06 **/ diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp index d5192e9b..9d0c7877 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp @@ -6,7 +6,7 @@ /** * @file TestHiCMAImplementationTLR.cpp * @brief Unit tests for the Tile Low Rank computation in the ExaGeoStat software package. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @date 2023-04-09 **/ diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index 4b696252..1bf702f0 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -134,7 +134,7 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp arguments_vector.push_back("--cores=" + to_string(cpu_size_distribution(gen))); // TODO: Till fixing cuda error with multiple devices. #ifdef USE_CUDA - arguments_vector.push_back("--gpus=" + "1"); + arguments_vector.push_back("--gpus=1"); #endif arguments_vector.push_back("--kernel=" + aKernelName); @@ -152,11 +152,11 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp arguments_vector.emplace_back("--idw"); arguments_vector.emplace_back("--distance_metric=" + aDistanceType); if (aKernelName.find("Bivariate") == string::npos && aKernelName.find("Trivariate") == string::npos) { - arguments_vector.emplace_back("--mloe-mmom"); arguments_vector.emplace_back("--fisher"); } if (aKernelName.find("Trivariate") == string::npos) { arguments_vector.emplace_back("--mspe"); + arguments_vector.emplace_back("--mloe-mmom"); } } From eea866b8381b19f7f4a21e3e539ef0e835a4d3db Mon Sep 17 00:00:00 2001 From: mahmoud Date: Thu, 11 Jan 2024 21:44:24 +0200 Subject: [PATCH 04/82] Fix cuda --- USER_MANUAL.md | 20 ++++++++++---------- cmake/ImportStarPu.cmake | 3 +-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 1cd0563d..3466a2d1 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -19,16 +19,16 @@ ExaGeoStatCPP User Manual ./config.sh -h ``` -* To Enable support of HiCMA, add `-H`. -* To enable examples, add `-e`. -* To enable tests, add `-t`. -* To enable heavy tests, add `-T`. -* To enable CUDA, add `-c`. -* To enable MPI, add `-m`. -* To enable verbose output, add `-v`. -* To change the installation path of the dependencies, use `-i `. -* To enable manually passing mkl as BLA vendor, add `-s`. -* To enable packaging system for distribution, add `-p`. +* To Enable support of HiCMA, add `-H` disabled by default. +* To enable examples, add `-e` enabled by default. +* To enable tests, add `-t` disabled by default. +* To enable heavy tests, add `-T` disabled by default. +* To enable CUDA, add `-c` disabled by default. +* To enable MPI, add `-m` disabled by default. +* To enable verbose output, add `-v` disabled by default. +* To change the installation path of the dependencies, use `-i ` project_path/installdir/_deps/ by default on Unix systems. +* To enable manually passing mkl as BLA vendor, add `-s` MKL by default. +* To enable packaging system for distribution, add `-p` disabled by default. ## Building diff --git a/cmake/ImportStarPu.cmake b/cmake/ImportStarPu.cmake index 86d661af..b160c574 100644 --- a/cmake/ImportStarPu.cmake +++ b/cmake/ImportStarPu.cmake @@ -31,7 +31,6 @@ set(auto_gen ON) set(url "https://gitlab.inria.fr/starpu/starpu.git") include(macros/ImportDependency) -message(" ${STARPU_COMPONENT_LIST}") -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" ${STARPU_COMPONENT_LIST} ${is_cmake} ${is_git} ${auto_gen}) +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "${STARPU_COMPONENT_LIST}" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") From 6884d5ceb9073f5e74c9c2786c6f0db5b980aae9 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Thu, 11 Jan 2024 23:42:37 +0200 Subject: [PATCH 05/82] fix for linking cuda --- USER_MANUAL.md | 8 ++++++-- cmake/ImportBLASPP.cmake | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 3466a2d1..e9d1e456 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -37,11 +37,15 @@ ExaGeoStatCPP User Manual ```commandline ./clean_build.sh -h ``` -* Run clean_build.sh to clean, Build and Install the project. +* Run clean_build.sh to build the project. +```commandline +./clean_build.sh +``` +* To build and install the project, Run the following command. ```commandline ./clean_build.sh -i ``` -* To enable verbose printing, Run the following command. +* To build and enable verbose printing, Run the following command. ```commandline ./clean_build.sh -v ``` diff --git a/cmake/ImportBLASPP.cmake b/cmake/ImportBLASPP.cmake index ed61c977..810166d7 100644 --- a/cmake/ImportBLASPP.cmake +++ b/cmake/ImportBLASPP.cmake @@ -17,7 +17,7 @@ set(tag "v2023.01.00") # Set installation flags if (USE_CUDA) set(flag "-Dgpu_backend=cuda") -else() +else () set(flag "-Dgpu_backend=") endif () @@ -30,4 +30,6 @@ set(url "https://github.com/icl-utk-edu/blaspp") set(${name}_DIR "${CMAKE_INSTALL_PREFIX}/${capital_name}/lib/cmake/${name}") include(macros/ImportDependency) ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) + +set(LIBS blaspp ${LIBS}) message(STATUS "${name} done") From cd7cda3fae6b6264ef2b4ca45008120308007cc9 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Fri, 12 Jan 2024 00:28:20 +0200 Subject: [PATCH 06/82] fix: hicma modifactions --- src/data-generators/concrete/SyntheticGenerator.cpp | 2 +- .../concrete/tile-low-rank/HicmaImplementation.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index 650e3a86..e2de00a9 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -68,7 +68,7 @@ SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aC if ( CHAMELEON_Comm_rank == 0 ){ DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), - aConfigurations.GetProblemSize(), aConfigurations.GetP(), path, + aConfigurations.GetProblemSize(), aConfigurations.GetVariablesNumber(), path, *data->GetLocations()); } delete[] pMatrix; diff --git a/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp index 037d2cfd..aeeb55bc 100644 --- a/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp @@ -103,7 +103,7 @@ T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware & this->SetContext(aHardware.GetContext(aConfigurations.GetComputation())); if (!aData->GetDescriptorData()->GetIsDescriptorInitiated()) { - this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(),aKernel.GetP(), apMeasurementsMatrix); + this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(),aKernel.GetVariablesNumber(), apMeasurementsMatrix); } // Create a Hicma sequence, if not initialized before through the same descriptors RUNTIME_request_t request_array[2] = {HICMA_REQUEST_INITIALIZER, HICMA_REQUEST_INITIALIZER}; @@ -131,7 +131,7 @@ T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware & int acc = aConfigurations.GetAccuracy(); if (iter_count == 0) { - this->SetModelingDescriptors(aData, aConfigurations, aKernel.GetP()); + this->SetModelingDescriptors(aData, aConfigurations, aKernel.GetVariablesNumber()); } auto *HICMA_descCUV = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, DescriptorName::DESCRIPTOR_CUV).hicma_desc; From f72d63cf0628f5cde2b677c2b878e6d8c4db2aab Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 16 Jan 2024 12:06:55 +0200 Subject: [PATCH 07/82] try fixing Jenkines --- Jenkinsfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index f38e9ab6..6107cb0f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -20,6 +20,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -42,6 +43,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -60,6 +62,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -82,6 +85,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -98,6 +102,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -129,4 +134,4 @@ pipeline { emailext body: "${env.JOB_NAME} - Please go to ${env.BUILD_URL}", subject: "Jenkins Pipeline build FAILED", recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']] } } -} \ No newline at end of file +} From 210c06bbbeccdeda891ca39a42e8d68a194485a8 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 16 Jan 2024 12:44:10 +0200 Subject: [PATCH 08/82] Adding R structure --- .gitignore | 684 ++++++++++++++++++ CMakeLists.txt | 21 +- DESCRIPTION | 23 + Jenkinsfile | 7 +- NAMESPACE | 3 + R/ExaGeoStatCPP.R | 0 cleanup | 27 + cmake/FindR.cmake | 176 +++++ config.sh | 33 +- configure | 71 ++ ...ementationDense.hpp => ChameleonDense.hpp} | 16 +- .../ChameleonDST.hpp} | 16 +- .../HicmaImplementation.hpp | 2 +- src/CMakeLists.txt | 4 +- .../LinearAlgebraFactory.cpp | 12 +- .../concrete/CMakeLists.txt | 8 +- ...ementationDense.cpp => ChameleonDense.cpp} | 8 +- .../ChameleonDST.cpp} | 16 +- .../HicmaImplementation.cpp | 4 +- tests/cpp-tests/CMakeLists.txt | 4 +- .../{data-generation => }/CMakeLists.txt | 4 +- ...figurations.cpp => TestConfigurations.cpp} | 20 +- 22 files changed, 1087 insertions(+), 72 deletions(-) create mode 100644 .gitignore create mode 100755 DESCRIPTION create mode 100644 NAMESPACE create mode 100755 R/ExaGeoStatCPP.R create mode 100755 cleanup create mode 100644 cmake/FindR.cmake create mode 100755 configure rename inst/include/linear-algebra-solvers/concrete/chameleon/dense/{ChameleonImplementationDense.hpp => ChameleonDense.hpp} (78%) rename inst/include/linear-algebra-solvers/concrete/chameleon/{diagonal-super-tile/ChameleonImplementationDST.hpp => dst/ChameleonDST.hpp} (87%) rename inst/include/linear-algebra-solvers/concrete/hicma/{tile-low-rank => tlr}/HicmaImplementation.hpp (99%) rename src/linear-algebra-solvers/concrete/chameleon/dense/{ChameleonImplementationDense.cpp => ChameleonDense.cpp} (74%) rename src/linear-algebra-solvers/concrete/chameleon/{diagonal-super-tile/ChameleonImplementationDST.cpp => dst/ChameleonDST.cpp} (90%) rename src/linear-algebra-solvers/concrete/{tile-low-rank => tlr}/HicmaImplementation.cpp (99%) rename tests/cpp-tests/configurations/{data-generation => }/CMakeLists.txt (79%) rename tests/cpp-tests/configurations/{data-generation/concrete/TestSyntheticDataConfigurations.cpp => TestConfigurations.cpp} (88%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f4e6b5c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,684 @@ +# Created by https://www.toptal.com/developers/gitignore/api/clion,intellij,eclipse,sublimetext,cmake,c++,c,cuda,codeblocks,opencv,jetbrains,python,pycharm +# Edit at https://www.toptal.com/developers/gitignore?templates=clion,intellij,eclipse,sublimetext,cmake,c++,c,cuda,codeblocks,opencv,jetbrains,python,pycharm + +# synthetic data for testing +tests/cpp-tests/data-generators/concrete/synthetic_ds + +# intallation directory +installdir/ + +# R files +.Rhistory + +# tar.gz files +*.tar.gz + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### C++ ### +# Prerequisites + +# Compiled Object files +*.slo + +# Precompiled Headers + +# Compiled Dynamic libraries + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai + +# Executables + +### CLion ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### CLion Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +### CodeBlocks ### +# specific to CodeBlocks IDE +*.layout +*.depend +# generated directories +bin/ +obj/ + +### CUDA ### +*.i +*.ii +*.gpu +*.ptx +*.cubin +*.fatbin + +### Eclipse ### +.metadata +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ +.apt_generated_test/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +# Uncomment this line if you wish to ignore the project description file. +# Typically, this file would be tracked if it contains build/dependency configurations: +#.project + +### Eclipse Patch ### +# Spring Boot Tooling +.sts4-cache/ + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### JetBrains Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream + +### OpenCV ### +#OpenCV for Mac and Linux +#build and release folders +*/CMakeFiles +*/CMakeCache.txt +*/Makefile +*/cmake_install.cmake +.DS_Store + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ +docs/html/ +docs/latex/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +### SublimeText ### +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +# Sesimic Toolbox Results Specifics +results/ +*.trace +*.segy +*.sgy +*.png +*.bin +data/*.segy +data/*.sgy +data/ +.idea/ +*.tar.bz2 +boost**/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f1b833d..6f692793 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ # The project is a parallel high performance unified framework for geographical statistics on manycore systems. # The file sets up variables and finds dependencies required for the project. # It also provides options to enable building tests, building examples, building documentation, and enabling a packaging system for distribution. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-01-30 @@ -24,6 +24,7 @@ option(BUILD_TESTS "Option to enable building tests" OFF) option(BUILD_HEAVY_TESTS "Option to enable building heavy tests, This may take a lot of time" OFF) option(BUILD_EXAMPLES "Option to enable building examples" ON) option(BUILD_DOCS "Build documentation in docs directory" ON) +option(USE_R "Enable the use of R and Rcpp in the project" OFF) option(CREATE_PACKAGE "Enable a packaging system for distribution" OFF) # Cmake Module Paths @@ -31,6 +32,8 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") # Select toolchain based on whether CUDA is enabled or not if (USE_CUDA) + message("") + message("---------------------------------------- CUDA") # Enable CUDA and include CudaToolchain add_definitions(-DUSE_CUDA=TRUE) enable_language(CUDA) @@ -59,6 +62,18 @@ add_definitions( -DKERNELS_PATH="${PROJECT_SOURCE_DIR}/inst/include/kernels/concrete/" ) +if (USE_R) + message("") + message("---------------------------------------- Rcpp") + # Find R and Rcpp using FindR Module + find_package(R REQUIRED) + if (${R_FOUND}) + message(STATUS "Using R technology") + list(APPEND LIBS "R") + add_definitions(-DUSING_R) + endif () +endif () + # ExaGeoStatCPP depends on a CUDA # ------------------------------- if (USE_CUDA) @@ -72,6 +87,8 @@ endif () # ExaGeoStatCPP depends on a MPI # ------------------------------- if (USE_MPI) + message("") + message("---------------------------------------- MPI") # Enable MPI and include MPI add_definitions(-DUSE_MPI=TRUE) message(STATUS "Trying to find MPI") @@ -83,6 +100,8 @@ endif () # ExaGeoStatCPP depends on LAPACKE #----------------------------- +message("") +message("---------------------------------------- LAPACKE") find_package(LAPACKE) list(APPEND LIBS ${LAPACKE_LIBRARIES}) link_directories(${LAPACKE_LIBRARY_DIRS_DEP}) diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100755 index 00000000..81112caf --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,23 @@ +Package: ExaGeoStatCPP +Type: Package +Title: R Package Demonstrates the R/C++ Language Interface for Exascale GeoStatistics software +Version: 1.1.0 +Date: 2024-01-14 +Author: Mahmoud ElKarargy [aut, cph], Sameh Abdulah [cre, cph], KAUST King Abdullah University of Science and Technology [fnd, cph], Brightskies [cph] +Maintainer: Sameh Abdulah , Mahmoud ElKarargy +Description: An R-wrapper for ExaGeoStatCPP: a parallel high performance unified framework for geostatistics on manycore systems. Its abbreviation stands for Exascale Geostatistics. The framework aims at optimizing the likelihood function for a given spatial data to provide an efficient way to predict missing observations. The framework targets many-core systems: clusters of CPUs and GPUs. +License: GPL (>= 3) +Imports: assertthat (>= 0.2.1), MASS +Depends: R (>= 3.5.0), assertthat (>= 0.2.1), MASS +RoxygenNote: 7.2.3 +SystemRequirements: CMake (>=3.20), C++ (>= 11), nlopt (>= 2.4.2 http://ab-initio.mit.edu), lapacke (https://github.com/xianyi/OpenBLAS/releases), blas (https://github.com/xianyi/OpenBLAS/releases), hwloc (>=1.11.5 https://www.open-mpi.org), gsl (>= 2.4 https://ftp.gnu.org) +NeedsCompilation: yes +OS_type: unix +Authors@R: c( + person("Mahmoud", "ElKarargy", role=c("aut", "cph"), email="mahmoud.elkarargy@brightskiesinc.com"), + person("Sameh", "Abdulah", role=c("cre","cph"), email="sameh.abdulah@kaust.edu.sa"), + person("KAUST", "King Abdullah University of Science and Technology", role=c("fnd","cph")), + person("Brightskies", role=c("cph")) + ) +URL: https://www.github.com/ecrc/ExaGeoStatCPP +Encoding: UTF-8 diff --git a/Jenkinsfile b/Jenkinsfile index 6107cb0f..f38e9ab6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -20,7 +20,6 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 - module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -43,7 +42,6 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 - module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -62,7 +60,6 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 - module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -85,7 +82,6 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 - module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -102,7 +98,6 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 - module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -134,4 +129,4 @@ pipeline { emailext body: "${env.JOB_NAME} - Please go to ${env.BUILD_URL}", subject: "Jenkins Pipeline build FAILED", recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']] } } -} +} \ No newline at end of file diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 00000000..7f899c19 --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,3 @@ +useDynLib(ExaGeoStatCPP, .registration=TRUE) +exportPattern("^[[:alpha:]]+") +import(methods, Rcpp) \ No newline at end of file diff --git a/R/ExaGeoStatCPP.R b/R/ExaGeoStatCPP.R new file mode 100755 index 00000000..e69de29b diff --git a/cleanup b/cleanup new file mode 100755 index 00000000..0a1885df --- /dev/null +++ b/cleanup @@ -0,0 +1,27 @@ +#! /bin/sh + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file cleanup +# @brief This file is intended to be used during 'R CMD build' command to delete all unnecessary files to match to CRAN policies. +# @version 1.1.0 +# @author David Helmy +# @author Mahmoud Elkarargy +# @date 2024-01-15 + +rm -rf ./src/*.so* +rm -rf ./src/*.d +rm -rf ./src/*.dll +rm -rf ./.idea/ +rm -rf ./cmake-build-debug +rm -rf ./bin/ +rm -rf ./.git/ +rm -rf ./config.sh +rm -rf ./clean_build.sh +rm -rf ./..Rcheck/ +rm -rf ./scripts/ +rm -rf ./installdir/ + +mv ./LICENSE.md ./LICENSE diff --git a/cmake/FindR.cmake b/cmake/FindR.cmake new file mode 100644 index 00000000..e6b740f9 --- /dev/null +++ b/cmake/FindR.cmake @@ -0,0 +1,176 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file FindR.cmake +# @brief Find the R and Rcpp library, Set some helpful variables. +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @author David Helmy +# @date 2024-01-14 + + +# Usage: +# find_package(R [REQUIRED] [QUIET] ) +# +# It sets the following variables: +# R _FOUND ... true if R and Rcpp is found on the system +# R_LIBRARIES ... full path to R and Rcpp library +# R_INCLUDE_DIRS ... R and Rcpp include directory +# +# The following variables will be checked by the function +# R_ROOT_PATH ... if set, the R libraries are exclusively searched +# under this path +# R_LIB_PATH ... if set, the Rcpp libraries are exclusively searched +# under this path + + +if (DEFINED ENV{R_HOME}) + set(R_ROOT_PATH "$ENV{R_HOME}") + +else () + execute_process(COMMAND R RHOME OUTPUT_VARIABLE R_HOME) + string(REGEX REPLACE "\n" "" R_HOME "${R_HOME}") + set(R_ROOT_PATH "${R_HOME}") + +endif () + +if (NOT R_INCLUDE_PATH) + execute_process(COMMAND ${R_ROOT_PATH}/bin/Rscript -e "cat(Sys.getenv('R_INCLUDE_DIR'))" OUTPUT_VARIABLE R_INCLUDE_DIR) + string(REGEX REPLACE "\n" "" R_INCLUDE_DIR "${R_INCLUDE_DIR}") + set(R_INCLUDE_PATH "${R_INCLUDE_DIR}") + message(STATUS "R Include Path : " ${R_INCLUDE_PATH}) +endif () + + +if (NOT RCPP_LIB_PATH) + execute_process( + COMMAND ${R_ROOT_PATH}/bin/Rscript -e "cat(find.package('Rcpp'))" + OUTPUT_VARIABLE RCPP_LIB_PATH + RESULT_VARIABLE RSCRIPT_RESULT + ) + + # Check if the command was successful + if (RSCRIPT_RESULT EQUAL 0) + # Trim whitespace from both ends of the output + string(STRIP ${RCPP_LIB_PATH} RCPP_LIB_PATH) + message(STATUS "RCPP_LIB_PATH: ${RCPP_LIB_PATH}") + else() + message(FATAL_ERROR "Error running Rscript. Exit code: ${RSCRIPT_RESULT}") + endif() +endif () + +message(STATUS "R Home Path : " ${R_ROOT_PATH}) + +if (R_ROOT_PATH) + + if (APPLE) + find_library( + R_LIB + REQUIRED + NAMES "libR.dylib" + PATHS ${R_ROOT_PATH} + PATH_SUFFIXES "lib" "lib64" "bin" + NO_DEFAULT_PATH + ) + else () + #find libs + find_library( + R_LIB + REQUIRED + NAMES "libR.so" + PATHS ${R_ROOT_PATH} + PATH_SUFFIXES "lib" "lib64" "bin" + NO_DEFAULT_PATH + ) + + endif () + +else () + error("R is not installed ") +endif (R_ROOT_PATH) + + +if (R_INCLUDE_PATH) + # find includes + find_path( + R_INCLUDE_DIRS + REQUIRED + NAMES "R.h" + PATHS ${R_INCLUDE_PATH} + PATH_SUFFIXES "include" + NO_DEFAULT_PATH + ) +endif () + +if (RCPP_LIB_PATH) + + #find libs + find_library( + RCPP_LIB + REQUIRED + NAMES "Rcpp.so" + PATHS ${RCPP_LIB_PATH} + PATH_SUFFIXES "/libs" "/lib64" "/bin" + NO_DEFAULT_PATH + ) + + # find includes + find_path( + RCPP_INCLUDE_DIRS + REQUIRED + NAMES "Rcpp.h" + PATHS ${RCPP_LIB_PATH} + PATH_SUFFIXES "/include" + NO_DEFAULT_PATH + ) + + +else () + + #find libs + find_library( + RCPP_LIB + REQUIRED + NAMES "Rcpp.so" + PATHS ${LIB_INSTALL_DIR} + ) + + #find includes + find_path( + RCPP_INCLUDE_DIRS + REQUIRED + NAMES "Rcpp.h" + PATHS ${INCLUDE_INSTALL_DIR} + ) + +endif (RCPP_LIB_PATH) + + +set(R_LIBRARIES + ${R_LIBRARIES} + ${R_LIB} + ) + + +set(R_INCLUDE + ${R_INCLUDE} + ${R_INCLUDE_DIRS} + ${RCPP_INCLUDE_DIRS} + ) + +add_library(R INTERFACE IMPORTED) +set_target_properties(R + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${R_INCLUDE}" + INTERFACE_LINK_LIBRARIES "${R_LIBRARIES}" + IMPORTED_LOCATION ${RCPP_LIB} + ) + + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(R DEFAULT_MSG + R_INCLUDE R_LIBRARIES) + +include_directories(${R_INCLUDE}) +mark_as_advanced(R_INCLUDE R_INCLUDE_DIRS RCPP_INCLUDE_DIRS R_LIBRARIES R_LIB RCPP_LIB) diff --git a/config.sh b/config.sh index b947b2ef..2c22f7ad 100755 --- a/config.sh +++ b/config.sh @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file config.sh -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-30 @@ -26,9 +26,12 @@ USE_CUDA="OFF" USE_MPI="OFF" BLAS_VENDOR="" PACKAGE="OFF" +SHOW_WARNINGS="OFF" +COMPILE_FLAGS="-Wl,--no-as-needed" +DEVELOPER_WARNINGS="-Wno-dev" # Parse command line options -while getopts ":tevhHi:cmspT" opt; do +while getopts ":tevhHi:cmspTw" opt; do case $opt in i) ##### Define installation path ##### echo -e "${YELLOW}Installation path set to $OPTARG.${NC}" @@ -52,15 +55,15 @@ while getopts ":tevhHi:cmspT" opt; do ;; c)##### Using cuda enabled ##### echo -e "${GREEN}Cuda enabled ${NC}" - USE_CUDA=ON + USE_CUDA="ON" ;; m)##### Using MPI enabled ##### echo -e "${GREEN}MPI enabled ${NC}" - USE_MPI=ON + USE_MPI="ON" ;; v) ##### printing full output of make ##### echo -e "${GREEN}printing make with details.${NC}" - VERBOSE=ON + VERBOSE="ON" ;; s) ##### Passing BLA vendor with mkl ##### echo -e "${GREEN}MKL as a BLA vendor${NC}" @@ -70,6 +73,10 @@ while getopts ":tevhHi:cmspT" opt; do echo -e "${GREEN}CPACK enabled${NC}" PACKAGE=ON ;; + w) ##### Enable showing all the warnings ##### + echo -e "${GREEN}Showing Warnings is enabled${NC}" + SHOW_WARNINGS="ON" + ;; \?) ##### Error unknown option ##### echo "Option $OPTARG parameter is unknown, please -h for help" exit 1 @@ -125,13 +132,21 @@ if [ -z "$USE_MPI" ]; then echo -e "${RED}Using MPI disabled${NC}" fi +if [ "$SHOW_WARNINGS" = "ON" ]; then + COMPILE_FLAGS+=" -W" + DEVELOPER_WARNINGS="" +elif [ "$SHOW_WARNINGS" = "OFF" ]; then + COMPILE_FLAGS+=" -w" +fi + echo "" echo -e "${YELLOW}Use -h to print the usages of exageostat-cpp flags.${NC}" echo "" rm -rf bin/ -mkdir -p bin/installdir +mkdir -p bin/ -cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ +cmake $DEVELOPER_WARNINGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_BUILD_TYPE=RELEASE \ -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ -DBUILD_TESTS="${BUILDING_TESTS}" \ -DBUILD_HEAVY_TESTS="${BUILDING_HEAVY_TESTS}" \ @@ -144,4 +159,6 @@ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCREATE_PACKAGE="${PACKAGE}" \ -H"${PROJECT_SOURCE_DIR}" \ -B"${PROJECT_SOURCE_DIR}/bin" \ - -G "Unix Makefiles" + -G "Unix Makefiles" \ + -DCMAKE_CXX_FLAGS_DEBUG="$COMPILE_FLAGS"\ + -DCMAKE_CXX_FLAGS_RELEASE="$COMPILE_FLAGS" diff --git a/configure b/configure new file mode 100755 index 00000000..f87744c2 --- /dev/null +++ b/configure @@ -0,0 +1,71 @@ +#! /bin/bash +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file config.sh +# @version 1.1.0 +# @author David Helmy +# @author Mahmoud ElKarargy +# @date 2024-01-15 + +cd "$(dirname "$0")" + +if [[ "$OSTYPE" == "darwin"* ]]; then + ABSOLUTE_PATH=$([[ $1 == /* ]] && echo "$1" || echo "$PWD/${1#./}") +else + ABSOLUTE_PATH=$(dirname $(realpath "$0")) +fi + +INSTALL_PREFIX=$PWD/installdir/_deps +BUILDING_TESTS="OFF" +BUILDING_HEAVY_TESTS="OFF" +BUILDING_EXAMPLES="OFF" +USING_HiCMA="OFF" +VERBOSE="OFF" +USE_CUDA="OFF" +USE_MPI="OFF" +BLAS_VENDOR="" +PACKAGE="OFF" +COMPILE_FLAGS="-Wl,--no-as-needed -w" +DEVELOPER_WARNINGS="-Wno-dev" + +rm -rf bin/ +mkdir bin/ + +cmake $DEVELOPER_WARNINGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_BUILD_TYPE=RELEASE \ + -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ + -DBUILD_TESTS="${BUILDING_TESTS}" \ + -DBUILD_HEAVY_TESTS="${BUILDING_HEAVY_TESTS}" \ + -DBUILD_EXAMPLES="${BUILDING_EXAMPLES}" \ + -DUSE_HICMA="${USING_HiCMA}" \ + -DCMAKE_VERBOSE_MAKEFILE:BOOL=${VERBOSE} \ + -DUSE_CUDA="${USE_CUDA}" \ + -DUSE_MPI="${USE_MPI}" \ + -DBLA_VENDOR="${BLAS_VENDOR}" \ + -DCREATE_PACKAGE="${PACKAGE}" \ + -H"${ABSOLUTE_PATH}" \ + -B"${ABSOLUTE_PATH}/bin" \ + -G "Unix Makefiles" \ + -DCMAKE_CXX_FLAGS_DEBUG="$COMPILE_FLAGS" \ + -DCMAKE_CXX_FLAGS_RELEASE="$COMPILE_FLAGS" + +# Change to the bin directory, or exit if it doesn't exist. +cd bin/ || { + echo "Error: bin directory not found." + exit 1 +} + +# Clean the directory and build the code with the specified options. +cmake --build . -j $(nproc) + +if [[ "$OSTYPE" == "darwin"* ]]; then + cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.dylib" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.dylib -> src" +else + echo "f eh??" + cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.so" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.so -> src" + ls ${ABSOLUTE_PATH}/src/ +fi + +rm -rf "${ABSOLUTE_PATH}/bin/" diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp similarity index 78% rename from inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.hpp rename to inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp index 8b3edb18..fc373763 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp @@ -7,14 +7,14 @@ * @file ChameleonImplementationDense.hpp * @brief This file contains the declaration of ChameleonImplementationDense class. * @details ChameleonImplementationDense is a concrete implementation of ChameleonImplementation class for dense matrices. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 **/ -#ifndef EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDENSE_HPP -#define EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDENSE_HPP +#ifndef EXAGEOSTATCPP_CHAMELEONDENSE_HPP +#define EXAGEOSTATCPP_CHAMELEONDENSE_HPP #include @@ -26,18 +26,18 @@ namespace exageostat::linearAlgebra::dense { * */ template - class ChameleonImplementationDense : public ChameleonImplementation { + class ChameleonDense : public ChameleonImplementation { public: /** * @brief Default constructor. */ - explicit ChameleonImplementationDense() = default; + explicit ChameleonDense() = default; /** * @brief Virtual destructor to allow calls to the correct concrete destructor. */ - ~ChameleonImplementationDense() override = default; + ~ChameleonDense() override = default; /** * @brief Computes the Cholesky factorization of a symmetric positive definite or Symmetric positive definite matrix. @@ -54,8 +54,8 @@ namespace exageostat::linearAlgebra::dense { * @tparam T Data Type: float or double * */ - EXAGEOSTAT_INSTANTIATE_CLASS(ChameleonImplementationDense) + EXAGEOSTAT_INSTANTIATE_CLASS(ChameleonDense) }//namespace exageostat -#endif //EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDENSE_HPP \ No newline at end of file +#endif //EXAGEOSTATCPP_CHAMELEONDENSE_HPP \ No newline at end of file diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp similarity index 87% rename from inst/include/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.hpp rename to inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp index a7991f3b..be3ed402 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp @@ -7,14 +7,14 @@ * @file ChameleonImplementationDST.hpp * @brief This file contains the declaration of ChameleonImplementationDST class. * @details ChameleonImplementationDST is a concrete implementation of LinearAlgebraMethods class for diagonal super tile matrices. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-26 **/ -#ifndef EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDST_HPP -#define EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDST_HPP +#ifndef EXAGEOSTATCPP_CHAMELEONDST_HPP +#define EXAGEOSTATCPP_CHAMELEONDST_HPP #include @@ -26,19 +26,19 @@ namespace exageostat::linearAlgebra::diagonalSuperTile { * */ template - class ChameleonImplementationDST : public ChameleonImplementation { + class ChameleonDST : public ChameleonImplementation { public: /** * @brief Default constructor. */ - explicit ChameleonImplementationDST() = default; + explicit ChameleonDST() = default; /** * @brief Virtual destructor to allow calls to the correct concrete destructor. */ - ~ChameleonImplementationDST() override = default; + ~ChameleonDST() override = default; /** * @brief Computes the Cholesky factorization of a symmetric positive definite or Symmetric positive definite matrix. @@ -79,8 +79,8 @@ namespace exageostat::linearAlgebra::diagonalSuperTile { * @tparam T Data Type: float or double * */ - EXAGEOSTAT_INSTANTIATE_CLASS(ChameleonImplementationDST) + EXAGEOSTAT_INSTANTIATE_CLASS(ChameleonDST) }//namespace exageostat -#endif //EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDST_HPP \ No newline at end of file +#endif //EXAGEOSTATCPP_CHAMELEONDST_HPP \ No newline at end of file diff --git a/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp similarity index 99% rename from inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp rename to inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp index 22fdf9bf..3f6dcf93 100644 --- a/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp @@ -7,7 +7,7 @@ * @file HicmaImplementation.hpp * @brief This file contains the declaration of HicmaImplementation class. * @details HicmaImplementation is a concrete implementation of LinearAlgebraMethods class for tile low-rank matrices. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-26 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0fa6e7d0..500c872e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,7 +27,7 @@ set(LIB_NAME ${PROJECT_NAME}) # Create the library with the specified source files and linking libraries. add_library(${LIB_NAME} - STATIC + SHARED ${SOURCES} ) target_compile_definitions(${LIB_NAME} PUBLIC ${COMPILE_DEFINITIONS}) @@ -50,4 +50,4 @@ install(TARGETS ${LIB_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib ) - install(EXPORT ${LIB_NAME}CoreConfig DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/cmake/) \ No newline at end of file +install(EXPORT ${LIB_NAME}CoreConfig DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/cmake/) \ No newline at end of file diff --git a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp index cfe6a88f..0707343f 100644 --- a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp @@ -8,7 +8,7 @@ * @brief Implementation of the LinearAlgebraFactory class for creating linear algebra solvers for different computations using HiCMA or Chameleon libraries. * The factory creates a unique pointer to a concrete implementation of the LinearAlgebraMethods class based on the computation specified. * If the required library is not enabled, it throws a runtime_error exception. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-03-20 **/ @@ -16,12 +16,12 @@ #include -#include -#include +#include +#include #ifdef USE_HICMA -#include +#include #endif @@ -35,7 +35,7 @@ std::unique_ptr> LinearAlgebraFactory::CreateLinearAl // Check the used Linear Algebra solver library, whether it's HiCMA or Chameleon. if (aComputation == EXACT_DENSE) { - return std::make_unique>(); + return std::make_unique>(); } // HiCMA Used @@ -47,7 +47,7 @@ std::unique_ptr> LinearAlgebraFactory::CreateLinearAl "Tile low rank generation isn't supported without enabling HiCMA. Use -DUSE_HICMA=ON"); #endif } else if (aComputation == DIAGONAL_APPROX) { - return std::make_unique>(); + return std::make_unique>(); } // Return nullptr if no computation is selected diff --git a/src/linear-algebra-solvers/concrete/CMakeLists.txt b/src/linear-algebra-solvers/concrete/CMakeLists.txt index cc84ec89..af1c7e3e 100644 --- a/src/linear-algebra-solvers/concrete/CMakeLists.txt +++ b/src/linear-algebra-solvers/concrete/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @brief CMake build script for the linear-algebra-solvers library, which includes the concrete implementations of the # LinearAlgebraMethods class based on the enabled libraries (HiCMA or Chameleon). # @author Mahmoud ElKarargy @@ -13,15 +13,15 @@ # Include the concrete implementations of the LinearAlgebraMethods class based on the enabled libraries (HiCMA or Chameleon) set(SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/chameleon/dense/ChameleonImplementationDense.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/chameleon/diagonal-super-tile/ChameleonImplementationDST.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/chameleon/dense/ChameleonDense.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/chameleon/dst/ChameleonDST.cpp ${CMAKE_CURRENT_SOURCE_DIR}/chameleon/ChameleonImplementation.cpp ${SOURCES} ) if (USE_HICMA) list(APPEND SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/tile-low-rank/HicmaImplementation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tlr/HicmaImplementation.cpp ) endif () diff --git a/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.cpp b/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp similarity index 74% rename from src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.cpp rename to src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp index c81d2d98..f29bc027 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp @@ -6,13 +6,13 @@ /** * @file ChameleonImplementationDense.cpp * @brief Dense Tile implementation of linear algebra methods. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 **/ -#include +#include using namespace std; @@ -20,8 +20,8 @@ using namespace exageostat::linearAlgebra::dense; template void -ChameleonImplementationDense::ExaGeoStatPotrfTile(const common::UpperLower &aUpperLower, void *apA, int aBand, - void *apCD, void *apCrk, const int &aMaxRank, const int &aAcc) { +ChameleonDense::ExaGeoStatPotrfTile(const common::UpperLower &aUpperLower, void *apA, int aBand, + void *apCD, void *apCrk, const int &aMaxRank, const int &aAcc) { int status = CHAMELEON_dpotrf_Tile((cham_uplo_t) aUpperLower, (CHAM_desc_t *) apA); if (status != CHAMELEON_SUCCESS) { throw std::runtime_error("CHAMELEON_dpotrf_Tile Failed, Matrix is not positive definite"); diff --git a/src/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.cpp b/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp similarity index 90% rename from src/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.cpp rename to src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp index 649520e8..2953d7c6 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp @@ -6,13 +6,13 @@ /** * @file ChameleonImplementationDST.cpp * @brief Diagonal Super Tile implementation of linear algebra methods. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 **/ -#include +#include using namespace std; @@ -20,8 +20,8 @@ using namespace exageostat::linearAlgebra::diagonalSuperTile; using namespace exageostat::common; template -void ChameleonImplementationDST::ExaGeoStatPotrfTile(const UpperLower &aUpperLower, void *apA, int aBand, void *apCD, - void *apCrk, const int &aMaxRank, const int &aAcc) { +void ChameleonDST::ExaGeoStatPotrfTile(const UpperLower &aUpperLower, void *apA, int aBand, void *apCD, + void *apCrk, const int &aMaxRank, const int &aAcc) { CHAM_context_t *chameleon_context; RUNTIME_sequence_t *sequence = nullptr; @@ -39,8 +39,8 @@ void ChameleonImplementationDST::ExaGeoStatPotrfTile(const UpperLower &aUpper } template -int ChameleonImplementationDST::ExaGeoStatPotrfDiagonalTileAsync(const common::UpperLower &aUpperLower, void *apA, - int aBand, void *apSequence, void *apRequest) { +int ChameleonDST::ExaGeoStatPotrfDiagonalTileAsync(const common::UpperLower &aUpperLower, void *apA, + int aBand, void *apSequence, void *apRequest) { CHAM_context_t *chameleon_context; chameleon_context = chameleon_context_self(); @@ -87,8 +87,8 @@ int ChameleonImplementationDST::ExaGeoStatPotrfDiagonalTileAsync(const common template -void ChameleonImplementationDST::ExaGeoStatParallelPotrfDiagonal(const common::UpperLower &aUpperLower, void *apA, - int aBand, void *apSequence, void *apRequest) { +void ChameleonDST::ExaGeoStatParallelPotrfDiagonal(const common::UpperLower &aUpperLower, void *apA, + int aBand, void *apSequence, void *apRequest) { CHAM_context_t *chameleon_context; RUNTIME_option_t options; diff --git a/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp similarity index 99% rename from src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp rename to src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp index aeeb55bc..699e8e04 100644 --- a/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp @@ -6,13 +6,13 @@ /** * @file HicmaImplementation.cpp * @brief Sets up the HiCMA descriptors needed for the tile low rank computations in ExaGeoStat. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-26 **/ -#include +#include using namespace std; diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index 6d9a940d..6d9d4430 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -4,14 +4,14 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-31 add_subdirectory(api) add_subdirectory(linear-algebra-solvers) add_subdirectory(data-generators) -add_subdirectory(configurations/data-generation) +add_subdirectory(configurations) add_subdirectory(kernels) add_subdirectory(helpers) diff --git a/tests/cpp-tests/configurations/data-generation/CMakeLists.txt b/tests/cpp-tests/configurations/CMakeLists.txt similarity index 79% rename from tests/cpp-tests/configurations/data-generation/CMakeLists.txt rename to tests/cpp-tests/configurations/CMakeLists.txt index bdcadc45..9b7867f7 100644 --- a/tests/cpp-tests/configurations/data-generation/CMakeLists.txt +++ b/tests/cpp-tests/configurations/CMakeLists.txt @@ -4,13 +4,13 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-31 set(EXAGEOSTAT_TESTFILES - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestSyntheticDataConfigurations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TestConfigurations.cpp ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE diff --git a/tests/cpp-tests/configurations/data-generation/concrete/TestSyntheticDataConfigurations.cpp b/tests/cpp-tests/configurations/TestConfigurations.cpp similarity index 88% rename from tests/cpp-tests/configurations/data-generation/concrete/TestSyntheticDataConfigurations.cpp rename to tests/cpp-tests/configurations/TestConfigurations.cpp index 0aa00c4e..12813246 100644 --- a/tests/cpp-tests/configurations/data-generation/concrete/TestSyntheticDataConfigurations.cpp +++ b/tests/cpp-tests/configurations/TestConfigurations.cpp @@ -4,21 +4,21 @@ // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). /** -* @file synthetic_data.cpp -* @brief Unit tests for the Configurations class in the ExaGeoStat software package. -* @details This file contains Catch2 unit tests that validate the functionality of the Configurations class -* in the ExaGeoStat software package. The tests cover various setters, getters, and value checks -* for configuration parameters such as dimensions, P-GRID, kernel name, problem size, precision, and more. -* Additionally, the tests include a copy-constructor test for the Configurations class. -* @version 1.0.0 -* @author Mahmoud ElKarargy -* @date 2023-01-31 + * @file synthetic_data.cpp + * @brief Unit tests for the Configurations class in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the Configurations class + * in the ExaGeoStat software package. The tests cover various setters, getters, and value checks + * for configuration parameters such as dimensions, P-GRID, kernel name, problem size, precision, and more. + * Additionally, the tests include a copy-constructor test for the Configurations class. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2023-01-31 **/ #include #include -#include +#include "configurations/Configurations.hpp" using namespace std; From 5b604ba60e667c0f02d46e50acf118447dd4a591 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 16 Jan 2024 13:29:19 +0200 Subject: [PATCH 09/82] fix:add doxygen in Jenkines --- DESCRIPTION | 2 +- Jenkinsfile | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 81112caf..26734fd3 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -7,7 +7,7 @@ Author: Mahmoud ElKarargy [aut, cph], Sameh Abdulah [cre, cph], KAUST King Abdul Maintainer: Sameh Abdulah , Mahmoud ElKarargy Description: An R-wrapper for ExaGeoStatCPP: a parallel high performance unified framework for geostatistics on manycore systems. Its abbreviation stands for Exascale Geostatistics. The framework aims at optimizing the likelihood function for a given spatial data to provide an efficient way to predict missing observations. The framework targets many-core systems: clusters of CPUs and GPUs. License: GPL (>= 3) -Imports: assertthat (>= 0.2.1), MASS +Imports: assertthat (>= 0.2.1), MASS, methods, Rcpp (>= 1.0.9) Depends: R (>= 3.5.0), assertthat (>= 0.2.1), MASS RoxygenNote: 7.2.3 SystemRequirements: CMake (>=3.20), C++ (>= 11), nlopt (>= 2.4.2 http://ab-initio.mit.edu), lapacke (https://github.com/xianyi/OpenBLAS/releases), blas (https://github.com/xianyi/OpenBLAS/releases), hwloc (>=1.11.5 https://www.open-mpi.org), gsl (>= 2.4 https://ftp.gnu.org) diff --git a/Jenkinsfile b/Jenkinsfile index f38e9ab6..353913a8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -20,6 +20,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -42,6 +43,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -60,6 +62,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -82,6 +85,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -98,6 +102,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### From b1f0fbf9cf6d9474cba94223cb5141d38c219499 Mon Sep 17 00:00:00 2001 From: Sameh Abdulah Date: Tue, 16 Jan 2024 15:44:34 +0300 Subject: [PATCH 10/82] add test file for pow-exp kernel --- tests/cpp-tests/kernels/CMakeLists.txt | 1 + .../TestUnivariatePowExpStationary.cpp | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp diff --git a/tests/cpp-tests/kernels/CMakeLists.txt b/tests/cpp-tests/kernels/CMakeLists.txt index cc96f6b4..25abf9aa 100644 --- a/tests/cpp-tests/kernels/CMakeLists.txt +++ b/tests/cpp-tests/kernels/CMakeLists.txt @@ -30,6 +30,7 @@ set(EXAGEOSTAT_TESTFILES ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestUnivariateExpNonGaussian.cpp ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestTrivariateMaternParsimonious.cpp ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestUnivariateMaternNonStat.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestUnivariatePowExpStationary.cpp ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE ) diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp new file mode 100644 index 00000000..2942d703 --- /dev/null +++ b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp @@ -0,0 +1,73 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestUnivariatePowExpStationary.cpp + * @brief Unit tests for the TestUnivariatePowExpStationary kernel in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariatePowExpStationary kernel + * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. + * @version 1.0.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-01-16 +**/ + +#include +#include +#include + +using namespace std; + +using namespace exageostat::configurations; +using namespace exageostat::api; +using namespace exageostat::common; +using namespace exageostat::hardware; + +void TEST_KERNEL_GENERATION_UnivariatePowExpStationary() { + + SECTION("UnivariatePowExpStationary") + { + + // Create a new synthetic_data_configurations object with the provided command line arguments + Configurations synthetic_data_configurations; + + int N = 9; + synthetic_data_configurations.SetProblemSize(N); + synthetic_data_configurations.SetKernelName("UnivariatePowExpStationary"); + synthetic_data_configurations.SetDimension(Dimension2D); + + vector initial_theta{1, 0.1, 0.5}; + synthetic_data_configurations.SetInitialTheta(initial_theta); + + int dts = 5; + synthetic_data_configurations.SetDenseTileSize(dts); + synthetic_data_configurations.SetComputation(EXACT_DENSE); + // initialize ExaGeoStat Hardware. + auto hardware = ExaGeoStatHardware(EXACT_DENSE, 3, 0); + + int seed = 0; + srand(seed); + exageostat::dataunits::ExaGeoStatData data; + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, + data); + + auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, + exageostat::common::DESCRIPTOR_Z).chameleon_desc; + auto *A = (double *) CHAM_descriptorZ->mat; + // Define the expected output + double expected_output_data[] = { -1.272336, -2.362813, 0.616384, -0.072468, 0.401498, -1.559690, 0.211848, + 0.776627, -1.524810}; + + for (size_t i = 0; i < N; i++) { + double diff = A[i] - expected_output_data[i]; + REQUIRE(diff == Catch::Approx(0.0).margin(1e-6)); + } + } +} + +TEST_CASE("Univariate Pow-Exp Stationary kernel test") { + TEST_KERNEL_GENERATION_UnivariatePowExpStationary(); + +} From b5d530e0dfefbaf269c1e0a51be562b2c5ee9e43 Mon Sep 17 00:00:00 2001 From: Mahmoud Karargy Date: Wed, 17 Jan 2024 16:55:32 +0300 Subject: [PATCH 11/82] test with -fpic --- cmake/ImportHCore.cmake | 2 +- cmake/ImportHiCMA.cmake | 2 +- cmake/ImportStarsH.cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/ImportHCore.cmake b/cmake/ImportHCore.cmake index 5f1c53cf..fec4e905 100644 --- a/cmake/ImportHCore.cmake +++ b/cmake/ImportHCore.cmake @@ -14,7 +14,7 @@ set(name "HCORE") set(tag "v0.1.3") set(version "0.1.3") -set(flag "") +set(flag -DCMAKE_C_FLAGS=-fPIC) set(is_cmake ON) set(is_git ON) set(auto_gen OFF) diff --git a/cmake/ImportHiCMA.cmake b/cmake/ImportHiCMA.cmake index 7257ee15..4d5e0c47 100644 --- a/cmake/ImportHiCMA.cmake +++ b/cmake/ImportHiCMA.cmake @@ -14,7 +14,7 @@ set(name "HICMA") set(tag "v1.0.0") set(version "1.0.0") -set(flag -DHICMA_USE_MPI=${USE_MPI}) +set(flag -DHICMA_USE_MPI=${USE_MPI} \-DCMAKE_C_FLAGS=-fPIC) set(is_cmake ON) set(is_git ON) set(auto_gen OFF) diff --git a/cmake/ImportStarsH.cmake b/cmake/ImportStarsH.cmake index b7c842b6..d9d27b56 100644 --- a/cmake/ImportStarsH.cmake +++ b/cmake/ImportStarsH.cmake @@ -14,7 +14,7 @@ set(name "STARSH") set(tag "v0.3.1") set(version "0.3.1") -set(flag \-DSTARPU=OFF \-DMPI=${USE_MPI}) +set(flag \-DSTARPU=OFF \-DMPI=${USE_MPI} \-DCMAKE_C_FLAGS=-fPIC) set(is_cmake ON) set(is_git ON) set(auto_gen OFF) From 957dd0c97b72565272ae0c02d5ad443aa064aaa2 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Wed, 17 Jan 2024 22:06:05 +0200 Subject: [PATCH 12/82] some modifications for HiCMA --- .gitignore | 685 ++++++++++++++++++ CHANGELOG.md | 1 + CMakeLists.txt | 8 + Jenkinsfile | 12 +- cmake/ImportBLASPP.cmake | 23 +- config.sh | 31 +- ...ementationDense.hpp => ChameleonDense.hpp} | 16 +- .../ChameleonDST.hpp} | 16 +- .../HicmaImplementation.hpp | 0 src/CMakeLists.txt | 2 +- .../LinearAlgebraFactory.cpp | 10 +- .../concrete/CMakeLists.txt | 6 +- ...ementationDense.cpp => ChameleonDense.cpp} | 8 +- .../ChameleonDST.cpp} | 16 +- .../HicmaImplementation.cpp | 2 +- tests/cpp-tests/CMakeLists.txt | 4 +- .../{data-generation => }/CMakeLists.txt | 4 +- ...figurations.cpp => TestConfigurations.cpp} | 18 +- 18 files changed, 781 insertions(+), 81 deletions(-) create mode 100644 .gitignore rename inst/include/linear-algebra-solvers/concrete/chameleon/dense/{ChameleonImplementationDense.hpp => ChameleonDense.hpp} (78%) rename inst/include/linear-algebra-solvers/concrete/chameleon/{diagonal-super-tile/ChameleonImplementationDST.hpp => dst/ChameleonDST.hpp} (87%) rename inst/include/linear-algebra-solvers/concrete/hicma/{tile-low-rank => tlr}/HicmaImplementation.hpp (100%) rename src/linear-algebra-solvers/concrete/chameleon/dense/{ChameleonImplementationDense.cpp => ChameleonDense.cpp} (74%) rename src/linear-algebra-solvers/concrete/chameleon/{diagonal-super-tile/ChameleonImplementationDST.cpp => dst/ChameleonDST.cpp} (90%) rename src/linear-algebra-solvers/concrete/{tile-low-rank => tlr}/HicmaImplementation.cpp (99%) rename tests/cpp-tests/configurations/{data-generation => }/CMakeLists.txt (79%) rename tests/cpp-tests/configurations/{data-generation/concrete/TestSyntheticDataConfigurations.cpp => TestConfigurations.cpp} (89%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..68debb78 --- /dev/null +++ b/.gitignore @@ -0,0 +1,685 @@ +# Created by https://www.toptal.com/developers/gitignore/api/clion,intellij,eclipse,sublimetext,cmake,c++,c,cuda,codeblocks,opencv,jetbrains,python,pycharm +# Edit at https://www.toptal.com/developers/gitignore?templates=clion,intellij,eclipse,sublimetext,cmake,c++,c,cuda,codeblocks,opencv,jetbrains,python,pycharm + +# synthetic data for testing +tests/cpp-tests/data-generators/concrete/synthetic_ds + +# intallation directory +installdir/ + +# R files +.Rhistory +.RData + +# tar.gz files +*.tar.gz + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### C++ ### +# Prerequisites + +# Compiled Object files +*.slo + +# Precompiled Headers + +# Compiled Dynamic libraries + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai + +# Executables + +### CLion ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### CLion Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +### CodeBlocks ### +# specific to CodeBlocks IDE +*.layout +*.depend +# generated directories +bin/ +obj/ + +### CUDA ### +*.i +*.ii +*.gpu +*.ptx +*.cubin +*.fatbin + +### Eclipse ### +.metadata +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ +.apt_generated_test/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +# Uncomment this line if you wish to ignore the project description file. +# Typically, this file would be tracked if it contains build/dependency configurations: +#.project + +### Eclipse Patch ### +# Spring Boot Tooling +.sts4-cache/ + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### JetBrains Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream + +### OpenCV ### +#OpenCV for Mac and Linux +#build and release folders +*/CMakeFiles +*/CMakeCache.txt +*/Makefile +*/cmake_install.cmake +.DS_Store + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ +docs/html/ +docs/latex/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +### SublimeText ### +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +# Sesimic Toolbox Results Specifics +results/ +*.trace +*.segy +*.sgy +*.png +*.bin +data/*.segy +data/*.sgy +data/ +.idea/ +*.tar.bz2 +boost**/ diff --git a/CHANGELOG.md b/CHANGELOG.md index e99c5df8..15c11f29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Implemented a new changelog. - Introduced a benchmarking script. +- .gitignore file ### Fixed - Resolved issues with MPI installation. diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f1b833d..b34b08c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,8 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") # Select toolchain based on whether CUDA is enabled or not if (USE_CUDA) + message("") + message("---------------------------------------- CUDA") # Enable CUDA and include CudaToolchain add_definitions(-DUSE_CUDA=TRUE) enable_language(CUDA) @@ -59,6 +61,8 @@ add_definitions( -DKERNELS_PATH="${PROJECT_SOURCE_DIR}/inst/include/kernels/concrete/" ) + + # ExaGeoStatCPP depends on a CUDA # ------------------------------- if (USE_CUDA) @@ -72,6 +76,8 @@ endif () # ExaGeoStatCPP depends on a MPI # ------------------------------- if (USE_MPI) + message("") + message("---------------------------------------- MPI") # Enable MPI and include MPI add_definitions(-DUSE_MPI=TRUE) message(STATUS "Trying to find MPI") @@ -83,6 +89,8 @@ endif () # ExaGeoStatCPP depends on LAPACKE #----------------------------- +message("") +message("---------------------------------------- LAPACKE") find_package(LAPACKE) list(APPEND LIBS ${LAPACKE_LIBRARIES}) link_directories(${LAPACKE_LIBRARY_DIRS_DEP}) diff --git a/Jenkinsfile b/Jenkinsfile index 6107cb0f..353913a8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -20,7 +20,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 - module load doxygen/1.8.20 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -43,7 +43,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 - module load doxygen/1.8.20 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -62,7 +62,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 - module load doxygen/1.8.20 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -85,7 +85,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 - module load doxygen/1.8.20 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -102,7 +102,7 @@ pipeline { module purge module load gcc/10.2.0 module load cmake/3.21.2 - module load doxygen/1.8.20 + module load doxygen/1.8.20 #################################################### # BLAS/LAPACK #################################################### @@ -134,4 +134,4 @@ pipeline { emailext body: "${env.JOB_NAME} - Please go to ${env.BUILD_URL}", subject: "Jenkins Pipeline build FAILED", recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']] } } -} +} \ No newline at end of file diff --git a/cmake/ImportBLASPP.cmake b/cmake/ImportBLASPP.cmake index 810166d7..1b2f7e29 100644 --- a/cmake/ImportBLASPP.cmake +++ b/cmake/ImportBLASPP.cmake @@ -12,24 +12,13 @@ include(ImportBLAS) #Configurations set(name blaspp) -string(TOUPPER ${name} capital_name) set(tag "v2023.01.00") -# Set installation flags -if (USE_CUDA) - set(flag "-Dgpu_backend=cuda") -else () - set(flag "-Dgpu_backend=") -endif () - -set(version "2023.01.00") -set(is_cmake ON) -set(is_git ON) -set(auto_gen OFF) set(url "https://github.com/icl-utk-edu/blaspp") - set(${name}_DIR "${CMAKE_INSTALL_PREFIX}/${capital_name}/lib/cmake/${name}") -include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) -set(LIBS blaspp ${LIBS}) -message(STATUS "${name} done") +include(FetchContent) +FetchContent_Declare(${name} GIT_REPOSITORY "${url}" GIT_TAG "${tag}") +FetchContent_MakeAvailable(${name}) + +set(LIBS ${name} ${LIBS}) +message(STATUS "${name} done") \ No newline at end of file diff --git a/config.sh b/config.sh index b947b2ef..04b62f5d 100755 --- a/config.sh +++ b/config.sh @@ -26,9 +26,12 @@ USE_CUDA="OFF" USE_MPI="OFF" BLAS_VENDOR="" PACKAGE="OFF" +SHOW_WARNINGS="OFF" +COMPILE_FLAGS="-Wl,--no-as-needed" +DEVELOPER_WARNINGS="-Wno-dev" # Parse command line options -while getopts ":tevhHi:cmspT" opt; do +while getopts ":tevhHi:cmspTw" opt; do case $opt in i) ##### Define installation path ##### echo -e "${YELLOW}Installation path set to $OPTARG.${NC}" @@ -52,15 +55,15 @@ while getopts ":tevhHi:cmspT" opt; do ;; c)##### Using cuda enabled ##### echo -e "${GREEN}Cuda enabled ${NC}" - USE_CUDA=ON + USE_CUDA="ON" ;; m)##### Using MPI enabled ##### echo -e "${GREEN}MPI enabled ${NC}" - USE_MPI=ON + USE_MPI="ON" ;; v) ##### printing full output of make ##### echo -e "${GREEN}printing make with details.${NC}" - VERBOSE=ON + VERBOSE="ON" ;; s) ##### Passing BLA vendor with mkl ##### echo -e "${GREEN}MKL as a BLA vendor${NC}" @@ -70,6 +73,10 @@ while getopts ":tevhHi:cmspT" opt; do echo -e "${GREEN}CPACK enabled${NC}" PACKAGE=ON ;; + w) ##### Enable showing all the warnings ##### + echo -e "${GREEN}Showing Warnings is enabled${NC}" + SHOW_WARNINGS="ON" + ;; \?) ##### Error unknown option ##### echo "Option $OPTARG parameter is unknown, please -h for help" exit 1 @@ -125,13 +132,21 @@ if [ -z "$USE_MPI" ]; then echo -e "${RED}Using MPI disabled${NC}" fi +if [ "$SHOW_WARNINGS" = "ON" ]; then + COMPILE_FLAGS+=" -W" + DEVELOPER_WARNINGS="" +elif [ "$SHOW_WARNINGS" = "OFF" ]; then + COMPILE_FLAGS+=" -w" +fi + echo "" echo -e "${YELLOW}Use -h to print the usages of exageostat-cpp flags.${NC}" echo "" rm -rf bin/ -mkdir -p bin/installdir +mkdir -p bin/ -cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ +cmake $DEVELOPER_WARNINGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_BUILD_TYPE=RELEASE \ -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ -DBUILD_TESTS="${BUILDING_TESTS}" \ -DBUILD_HEAVY_TESTS="${BUILDING_HEAVY_TESTS}" \ @@ -144,4 +159,6 @@ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCREATE_PACKAGE="${PACKAGE}" \ -H"${PROJECT_SOURCE_DIR}" \ -B"${PROJECT_SOURCE_DIR}/bin" \ - -G "Unix Makefiles" + -G "Unix Makefiles" \ + -DCMAKE_CXX_FLAGS_DEBUG="$COMPILE_FLAGS"\ + -DCMAKE_CXX_FLAGS_RELEASE="$COMPILE_FLAGS" diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp similarity index 78% rename from inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.hpp rename to inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp index 8b3edb18..b902e8ab 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp @@ -7,14 +7,14 @@ * @file ChameleonImplementationDense.hpp * @brief This file contains the declaration of ChameleonImplementationDense class. * @details ChameleonImplementationDense is a concrete implementation of ChameleonImplementation class for dense matrices. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 **/ -#ifndef EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDENSE_HPP -#define EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDENSE_HPP +#ifndef EXAGEOSTATCPP_CHAMELEONDENSE_HPP +#define EXAGEOSTATCPP_CHAMELEONDENSE_HPP #include @@ -26,18 +26,18 @@ namespace exageostat::linearAlgebra::dense { * */ template - class ChameleonImplementationDense : public ChameleonImplementation { + class ChameleonDense : public ChameleonImplementation { public: /** * @brief Default constructor. */ - explicit ChameleonImplementationDense() = default; + explicit ChameleonDense() = default; /** * @brief Virtual destructor to allow calls to the correct concrete destructor. */ - ~ChameleonImplementationDense() override = default; + ~ChameleonDense() override = default; /** * @brief Computes the Cholesky factorization of a symmetric positive definite or Symmetric positive definite matrix. @@ -54,8 +54,8 @@ namespace exageostat::linearAlgebra::dense { * @tparam T Data Type: float or double * */ - EXAGEOSTAT_INSTANTIATE_CLASS(ChameleonImplementationDense) + EXAGEOSTAT_INSTANTIATE_CLASS(ChameleonDense) }//namespace exageostat -#endif //EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDENSE_HPP \ No newline at end of file +#endif //EXAGEOSTATCPP_CHAMELEONDENSE_HPP \ No newline at end of file diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp similarity index 87% rename from inst/include/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.hpp rename to inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp index a7991f3b..544dbb97 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp @@ -7,14 +7,14 @@ * @file ChameleonImplementationDST.hpp * @brief This file contains the declaration of ChameleonImplementationDST class. * @details ChameleonImplementationDST is a concrete implementation of LinearAlgebraMethods class for diagonal super tile matrices. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-26 **/ -#ifndef EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDST_HPP -#define EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDST_HPP +#ifndef EXAGEOSTATCPP_CHAMELEONDST_HPP +#define EXAGEOSTATCPP_CHAMELEONDST_HPP #include @@ -26,19 +26,19 @@ namespace exageostat::linearAlgebra::diagonalSuperTile { * */ template - class ChameleonImplementationDST : public ChameleonImplementation { + class ChameleonDST : public ChameleonImplementation { public: /** * @brief Default constructor. */ - explicit ChameleonImplementationDST() = default; + explicit ChameleonDST() = default; /** * @brief Virtual destructor to allow calls to the correct concrete destructor. */ - ~ChameleonImplementationDST() override = default; + ~ChameleonDST() override = default; /** * @brief Computes the Cholesky factorization of a symmetric positive definite or Symmetric positive definite matrix. @@ -79,8 +79,8 @@ namespace exageostat::linearAlgebra::diagonalSuperTile { * @tparam T Data Type: float or double * */ - EXAGEOSTAT_INSTANTIATE_CLASS(ChameleonImplementationDST) + EXAGEOSTAT_INSTANTIATE_CLASS(ChameleonDST) }//namespace exageostat -#endif //EXAGEOSTATCPP_CHAMELEONIMPLEMENTATIONDST_HPP \ No newline at end of file +#endif //EXAGEOSTATCPP_CHAMELEONDST_HPP \ No newline at end of file diff --git a/inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp similarity index 100% rename from inst/include/linear-algebra-solvers/concrete/hicma/tile-low-rank/HicmaImplementation.hpp rename to inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0fa6e7d0..ecad6074 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,4 +50,4 @@ install(TARGETS ${LIB_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib ) - install(EXPORT ${LIB_NAME}CoreConfig DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/cmake/) \ No newline at end of file +install(EXPORT ${LIB_NAME}CoreConfig DESTINATION ${CMAKE_INSTALL_PREFIX}/EXAGEOSTATCPP/lib/cmake/) \ No newline at end of file diff --git a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp index cfe6a88f..64949703 100644 --- a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp @@ -16,12 +16,12 @@ #include -#include -#include +#include +#include #ifdef USE_HICMA -#include +#include #endif @@ -35,7 +35,7 @@ std::unique_ptr> LinearAlgebraFactory::CreateLinearAl // Check the used Linear Algebra solver library, whether it's HiCMA or Chameleon. if (aComputation == EXACT_DENSE) { - return std::make_unique>(); + return std::make_unique>(); } // HiCMA Used @@ -47,7 +47,7 @@ std::unique_ptr> LinearAlgebraFactory::CreateLinearAl "Tile low rank generation isn't supported without enabling HiCMA. Use -DUSE_HICMA=ON"); #endif } else if (aComputation == DIAGONAL_APPROX) { - return std::make_unique>(); + return std::make_unique>(); } // Return nullptr if no computation is selected diff --git a/src/linear-algebra-solvers/concrete/CMakeLists.txt b/src/linear-algebra-solvers/concrete/CMakeLists.txt index cc84ec89..d2ef8862 100644 --- a/src/linear-algebra-solvers/concrete/CMakeLists.txt +++ b/src/linear-algebra-solvers/concrete/CMakeLists.txt @@ -13,15 +13,15 @@ # Include the concrete implementations of the LinearAlgebraMethods class based on the enabled libraries (HiCMA or Chameleon) set(SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/chameleon/dense/ChameleonImplementationDense.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/chameleon/diagonal-super-tile/ChameleonImplementationDST.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/chameleon/dense/ChameleonDense.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/chameleon/dst/ChameleonDST.cpp ${CMAKE_CURRENT_SOURCE_DIR}/chameleon/ChameleonImplementation.cpp ${SOURCES} ) if (USE_HICMA) list(APPEND SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/tile-low-rank/HicmaImplementation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tlr/HicmaImplementation.cpp ) endif () diff --git a/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.cpp b/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp similarity index 74% rename from src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.cpp rename to src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp index c81d2d98..17009ca8 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonImplementationDense.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp @@ -6,13 +6,13 @@ /** * @file ChameleonImplementationDense.cpp * @brief Dense Tile implementation of linear algebra methods. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 **/ -#include +#include using namespace std; @@ -20,8 +20,8 @@ using namespace exageostat::linearAlgebra::dense; template void -ChameleonImplementationDense::ExaGeoStatPotrfTile(const common::UpperLower &aUpperLower, void *apA, int aBand, - void *apCD, void *apCrk, const int &aMaxRank, const int &aAcc) { +ChameleonDense::ExaGeoStatPotrfTile(const common::UpperLower &aUpperLower, void *apA, int aBand, + void *apCD, void *apCrk, const int &aMaxRank, const int &aAcc) { int status = CHAMELEON_dpotrf_Tile((cham_uplo_t) aUpperLower, (CHAM_desc_t *) apA); if (status != CHAMELEON_SUCCESS) { throw std::runtime_error("CHAMELEON_dpotrf_Tile Failed, Matrix is not positive definite"); diff --git a/src/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.cpp b/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp similarity index 90% rename from src/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.cpp rename to src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp index 649520e8..b243f58e 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/diagonal-super-tile/ChameleonImplementationDST.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp @@ -6,13 +6,13 @@ /** * @file ChameleonImplementationDST.cpp * @brief Diagonal Super Tile implementation of linear algebra methods. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 **/ -#include +#include using namespace std; @@ -20,8 +20,8 @@ using namespace exageostat::linearAlgebra::diagonalSuperTile; using namespace exageostat::common; template -void ChameleonImplementationDST::ExaGeoStatPotrfTile(const UpperLower &aUpperLower, void *apA, int aBand, void *apCD, - void *apCrk, const int &aMaxRank, const int &aAcc) { +void ChameleonDST::ExaGeoStatPotrfTile(const UpperLower &aUpperLower, void *apA, int aBand, void *apCD, + void *apCrk, const int &aMaxRank, const int &aAcc) { CHAM_context_t *chameleon_context; RUNTIME_sequence_t *sequence = nullptr; @@ -39,8 +39,8 @@ void ChameleonImplementationDST::ExaGeoStatPotrfTile(const UpperLower &aUpper } template -int ChameleonImplementationDST::ExaGeoStatPotrfDiagonalTileAsync(const common::UpperLower &aUpperLower, void *apA, - int aBand, void *apSequence, void *apRequest) { +int ChameleonDST::ExaGeoStatPotrfDiagonalTileAsync(const common::UpperLower &aUpperLower, void *apA, + int aBand, void *apSequence, void *apRequest) { CHAM_context_t *chameleon_context; chameleon_context = chameleon_context_self(); @@ -87,8 +87,8 @@ int ChameleonImplementationDST::ExaGeoStatPotrfDiagonalTileAsync(const common template -void ChameleonImplementationDST::ExaGeoStatParallelPotrfDiagonal(const common::UpperLower &aUpperLower, void *apA, - int aBand, void *apSequence, void *apRequest) { +void ChameleonDST::ExaGeoStatParallelPotrfDiagonal(const common::UpperLower &aUpperLower, void *apA, + int aBand, void *apSequence, void *apRequest) { CHAM_context_t *chameleon_context; RUNTIME_option_t options; diff --git a/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp similarity index 99% rename from src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp rename to src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp index aeeb55bc..370d4402 100644 --- a/src/linear-algebra-solvers/concrete/tile-low-rank/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp @@ -12,7 +12,7 @@ * @date 2023-03-26 **/ -#include +#include using namespace std; diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index 6d9a940d..cff029b3 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -4,14 +4,14 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @date 2023-01-31 add_subdirectory(api) add_subdirectory(linear-algebra-solvers) add_subdirectory(data-generators) -add_subdirectory(configurations/data-generation) +add_subdirectory(configurations) add_subdirectory(kernels) add_subdirectory(helpers) diff --git a/tests/cpp-tests/configurations/data-generation/CMakeLists.txt b/tests/cpp-tests/configurations/CMakeLists.txt similarity index 79% rename from tests/cpp-tests/configurations/data-generation/CMakeLists.txt rename to tests/cpp-tests/configurations/CMakeLists.txt index bdcadc45..5c54869a 100644 --- a/tests/cpp-tests/configurations/data-generation/CMakeLists.txt +++ b/tests/cpp-tests/configurations/CMakeLists.txt @@ -4,13 +4,13 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy # @date 2023-01-31 set(EXAGEOSTAT_TESTFILES - ${CMAKE_CURRENT_SOURCE_DIR}/concrete/TestSyntheticDataConfigurations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TestConfigurations.cpp ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE diff --git a/tests/cpp-tests/configurations/data-generation/concrete/TestSyntheticDataConfigurations.cpp b/tests/cpp-tests/configurations/TestConfigurations.cpp similarity index 89% rename from tests/cpp-tests/configurations/data-generation/concrete/TestSyntheticDataConfigurations.cpp rename to tests/cpp-tests/configurations/TestConfigurations.cpp index 0aa00c4e..7e057335 100644 --- a/tests/cpp-tests/configurations/data-generation/concrete/TestSyntheticDataConfigurations.cpp +++ b/tests/cpp-tests/configurations/TestConfigurations.cpp @@ -4,15 +4,15 @@ // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). /** -* @file synthetic_data.cpp -* @brief Unit tests for the Configurations class in the ExaGeoStat software package. -* @details This file contains Catch2 unit tests that validate the functionality of the Configurations class -* in the ExaGeoStat software package. The tests cover various setters, getters, and value checks -* for configuration parameters such as dimensions, P-GRID, kernel name, problem size, precision, and more. -* Additionally, the tests include a copy-constructor test for the Configurations class. -* @version 1.0.0 -* @author Mahmoud ElKarargy -* @date 2023-01-31 + * @file synthetic_data.cpp + * @brief Unit tests for the Configurations class in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the Configurations class + * in the ExaGeoStat software package. The tests cover various setters, getters, and value checks + * for configuration parameters such as dimensions, P-GRID, kernel name, problem size, precision, and more. + * Additionally, the tests include a copy-constructor test for the Configurations class. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2023-01-31 **/ #include From fe1f8935608ef3727f5685d5d8e9131fbc4f402a Mon Sep 17 00:00:00 2001 From: mahmoud Date: Thu, 18 Jan 2024 18:43:17 +0200 Subject: [PATCH 13/82] updates --- .gitignore | 1 + CHANGELOG.md | 1 + CMakeLists.txt | 31 +++++++++++++------- R/ExaGeoStatCPP.R | 2 ++ cmake/FindR.cmake | 2 +- cmake/ImportBLASPP.cmake | 23 ++++----------- cmake/ImportHCore.cmake | 2 +- cmake/ImportHiCMA.cmake | 2 +- cmake/ImportStarsH.cmake | 2 +- cmake/macros/BuildDependency.cmake | 2 +- config.sh | 1 + configure | 6 ++-- inst/include/hardware/ExaGeoStatHardware.hpp | 1 + src/CMakeLists.txt | 19 ++++++++---- src/DataTypeModule.cpp | 29 ++++++++++++++++++ src/RcppExports.cpp | 30 +++++++++++++++++++ src/adapters/CMakeLists.txt | 17 +++++++++++ src/hardware/ExaGeoStatHardware.cpp | 4 +++ 18 files changed, 133 insertions(+), 42 deletions(-) create mode 100644 src/DataTypeModule.cpp create mode 100644 src/RcppExports.cpp create mode 100644 src/adapters/CMakeLists.txt diff --git a/.gitignore b/.gitignore index f4e6b5c8..68debb78 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ installdir/ # R files .Rhistory +.RData # tar.gz files *.tar.gz diff --git a/CHANGELOG.md b/CHANGELOG.md index e99c5df8..15c11f29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Implemented a new changelog. - Introduced a benchmarking script. +- .gitignore file ### Fixed - Resolved issues with MPI installation. diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f692793..b8e177ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,17 +62,7 @@ add_definitions( -DKERNELS_PATH="${PROJECT_SOURCE_DIR}/inst/include/kernels/concrete/" ) -if (USE_R) - message("") - message("---------------------------------------- Rcpp") - # Find R and Rcpp using FindR Module - find_package(R REQUIRED) - if (${R_FOUND}) - message(STATUS "Using R technology") - list(APPEND LIBS "R") - add_definitions(-DUSING_R) - endif () -endif () + # ExaGeoStatCPP depends on a CUDA # ------------------------------- @@ -165,6 +155,25 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prerequisites) set(MY_LOGGER_PATH ${CMAKE_CURRENT_SOURCE_DIR}) add_definitions(-DMY_LOGGER_PATH="${CMAKE_CURRENT_SOURCE_DIR}") +if (${BUILD_SHARED_LIBS}) + set(BLA_STATIC OFF) +else () + set(BLA_STATIC ON) +endif () + +if (USE_R) + message("") + message("---------------------------------------- Rcpp") + # Find R and Rcpp using FindR Module + find_package(R REQUIRED) + if (${R_FOUND}) + message(STATUS "Using R technology") + list(APPEND LIBS R) + add_definitions(-DUSING_R) + endif () +endif () + + # Add src Directory to expose added libraries add_subdirectory(src) diff --git a/R/ExaGeoStatCPP.R b/R/ExaGeoStatCPP.R index e69de29b..969f73a2 100755 --- a/R/ExaGeoStatCPP.R +++ b/R/ExaGeoStatCPP.R @@ -0,0 +1,2 @@ + + loadModule("ExaGeoStatCPP", TRUE) diff --git a/cmake/FindR.cmake b/cmake/FindR.cmake index e6b740f9..f095bbf2 100644 --- a/cmake/FindR.cmake +++ b/cmake/FindR.cmake @@ -167,7 +167,7 @@ set_target_properties(R IMPORTED_LOCATION ${RCPP_LIB} ) - +message("${RCPP_INCLUDE_DIRS}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(R DEFAULT_MSG R_INCLUDE R_LIBRARIES) diff --git a/cmake/ImportBLASPP.cmake b/cmake/ImportBLASPP.cmake index 810166d7..1b2f7e29 100644 --- a/cmake/ImportBLASPP.cmake +++ b/cmake/ImportBLASPP.cmake @@ -12,24 +12,13 @@ include(ImportBLAS) #Configurations set(name blaspp) -string(TOUPPER ${name} capital_name) set(tag "v2023.01.00") -# Set installation flags -if (USE_CUDA) - set(flag "-Dgpu_backend=cuda") -else () - set(flag "-Dgpu_backend=") -endif () - -set(version "2023.01.00") -set(is_cmake ON) -set(is_git ON) -set(auto_gen OFF) set(url "https://github.com/icl-utk-edu/blaspp") - set(${name}_DIR "${CMAKE_INSTALL_PREFIX}/${capital_name}/lib/cmake/${name}") -include(macros/ImportDependency) -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) -set(LIBS blaspp ${LIBS}) -message(STATUS "${name} done") +include(FetchContent) +FetchContent_Declare(${name} GIT_REPOSITORY "${url}" GIT_TAG "${tag}") +FetchContent_MakeAvailable(${name}) + +set(LIBS ${name} ${LIBS}) +message(STATUS "${name} done") \ No newline at end of file diff --git a/cmake/ImportHCore.cmake b/cmake/ImportHCore.cmake index fec4e905..5f1c53cf 100644 --- a/cmake/ImportHCore.cmake +++ b/cmake/ImportHCore.cmake @@ -14,7 +14,7 @@ set(name "HCORE") set(tag "v0.1.3") set(version "0.1.3") -set(flag -DCMAKE_C_FLAGS=-fPIC) +set(flag "") set(is_cmake ON) set(is_git ON) set(auto_gen OFF) diff --git a/cmake/ImportHiCMA.cmake b/cmake/ImportHiCMA.cmake index 4d5e0c47..7257ee15 100644 --- a/cmake/ImportHiCMA.cmake +++ b/cmake/ImportHiCMA.cmake @@ -14,7 +14,7 @@ set(name "HICMA") set(tag "v1.0.0") set(version "1.0.0") -set(flag -DHICMA_USE_MPI=${USE_MPI} \-DCMAKE_C_FLAGS=-fPIC) +set(flag -DHICMA_USE_MPI=${USE_MPI}) set(is_cmake ON) set(is_git ON) set(auto_gen OFF) diff --git a/cmake/ImportStarsH.cmake b/cmake/ImportStarsH.cmake index d9d27b56..b7c842b6 100644 --- a/cmake/ImportStarsH.cmake +++ b/cmake/ImportStarsH.cmake @@ -14,7 +14,7 @@ set(name "STARSH") set(tag "v0.3.1") set(version "0.3.1") -set(flag \-DSTARPU=OFF \-DMPI=${USE_MPI} \-DCMAKE_C_FLAGS=-fPIC) +set(flag \-DSTARPU=OFF \-DMPI=${USE_MPI}) set(is_cmake ON) set(is_git ON) set(auto_gen OFF) diff --git a/cmake/macros/BuildDependency.cmake b/cmake/macros/BuildDependency.cmake index 825db78b..90375b4d 100644 --- a/cmake/macros/BuildDependency.cmake +++ b/cmake/macros/BuildDependency.cmake @@ -59,7 +59,7 @@ macro(BuildDependency raw_name url tag flags is_using_cmake is_using_git auto_ge # Configure subproject. if (${is_using_cmake}) - execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/${capital_name} ${flags} + execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/${capital_name} -DCMAKE_C_FLAGS=-fPIC ${flags} ${${name}_srcpath} WORKING_DIRECTORY ${${name}_binpath}) else () diff --git a/config.sh b/config.sh index 2c22f7ad..e00b7979 100755 --- a/config.sh +++ b/config.sh @@ -157,6 +157,7 @@ cmake $DEVELOPER_WARNINGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DUSE_MPI="${USE_MPI}" \ -DBLA_VENDOR="${BLAS_VENDOR}" \ -DCREATE_PACKAGE="${PACKAGE}" \ + -DBUILD_SHARED_LIBS=OFF \ -H"${PROJECT_SOURCE_DIR}" \ -B"${PROJECT_SOURCE_DIR}/bin" \ -G "Unix Makefiles" \ diff --git a/configure b/configure index f87744c2..f1e8faba 100755 --- a/configure +++ b/configure @@ -27,7 +27,7 @@ USE_CUDA="OFF" USE_MPI="OFF" BLAS_VENDOR="" PACKAGE="OFF" -COMPILE_FLAGS="-Wl,--no-as-needed -w" +COMPILE_FLAGS="-Wl,--no-as-needed -w -fpic" DEVELOPER_WARNINGS="-Wno-dev" rm -rf bin/ @@ -43,8 +43,10 @@ cmake $DEVELOPER_WARNINGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCMAKE_VERBOSE_MAKEFILE:BOOL=${VERBOSE} \ -DUSE_CUDA="${USE_CUDA}" \ -DUSE_MPI="${USE_MPI}" \ + -DUSE_R=ON \ -DBLA_VENDOR="${BLAS_VENDOR}" \ -DCREATE_PACKAGE="${PACKAGE}" \ + -DBUILD_SHARED_LIBS=OFF \ -H"${ABSOLUTE_PATH}" \ -B"${ABSOLUTE_PATH}/bin" \ -G "Unix Makefiles" \ @@ -63,9 +65,7 @@ cmake --build . -j $(nproc) if [[ "$OSTYPE" == "darwin"* ]]; then cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.dylib" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.dylib -> src" else - echo "f eh??" cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.so" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.so -> src" - ls ${ABSOLUTE_PATH}/src/ fi rm -rf "${ABSOLUTE_PATH}/bin/" diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index 8ed894a5..38e8d239 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -33,6 +33,7 @@ namespace exageostat::hardware { * */ ExaGeoStatHardware(const common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); + ExaGeoStatHardware(const int &aCoreNumber, const int &aGpuNumber); /** * @brief Destructor for ExaGeoStatHardware. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 500c872e..5a00e3c2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,17 +21,24 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/helpers) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/hardware) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/prediction) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/results) +#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/adapters) + +if (USE_R) + set( + SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/DataTypeModule.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/RcppExports.cpp + ${SOURCES} + ) +endif () # Set the name of the library to be created. set(LIB_NAME ${PROJECT_NAME}) - # Create the library with the specified source files and linking libraries. -add_library(${LIB_NAME} - SHARED - ${SOURCES} - ) +add_library(${LIB_NAME} SHARED ${SOURCES}) + target_compile_definitions(${LIB_NAME} PUBLIC ${COMPILE_DEFINITIONS}) -target_link_libraries(${LIB_NAME} PUBLIC ${LIBS}) +target_link_libraries(${LIB_NAME} ${LIBS}) # Set the version of the library. set_target_properties(${LIB_NAME} diff --git a/src/DataTypeModule.cpp b/src/DataTypeModule.cpp new file mode 100644 index 00000000..70da2481 --- /dev/null +++ b/src/DataTypeModule.cpp @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2023, King Abdullah University of Science and Technology + * All rights reserved. + * + * MPCR is an R package provided by the STSDS group at KAUST + * + **/ +#include +#include + +/** Expose C++ class to R to be able to use Wrap and As + * Allows C++ to Send and Receive Class object from R + **/ +RCPP_EXPOSED_CLASS(ExaGeoStatHardware) + +/** Expose C++ Object With the Given functions **/ +RCPP_MODULE(ExaGeoStatCPP) { + + /** MPCR Class **/ + using namespace Rcpp; + + + /** Basic Utilities **/ + class_ ("Hardware") + .constructor (); + + /** Function that are not masked **/ + +} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp new file mode 100644 index 00000000..b3f0752c --- /dev/null +++ b/src/RcppExports.cpp @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2023, King Abdullah University of Science and Technology + * All rights reserved. + * + * MPCR is an R package provided by the STSDS group at KAUST + * + **/ + +#include + + +using namespace Rcpp; + +#ifdef RCPP_USE_GLOBAL_ROSTREAM +Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); +Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); +#endif + + +RcppExport SEXP _rcpp_module_boot_ExaGeoStatCPP(); + +static const R_CallMethodDef CallEntries[] = { + {"_rcpp_module_boot_ExaGeoStatCPP", (DL_FUNC) &_rcpp_module_boot_ExaGeoStatCPP, 0}, + {NULL, NULL, 0} +}; + +RcppExport void R_init_ExaGeoStatCPP(DllInfo *dll) { + R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); + R_useDynamicSymbols(dll, FALSE); +} diff --git a/src/adapters/CMakeLists.txt b/src/adapters/CMakeLists.txt new file mode 100644 index 00000000..1248e066 --- /dev/null +++ b/src/adapters/CMakeLists.txt @@ -0,0 +1,17 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @brief CMake build script for the adapters library. +# @author Mahmoud ElKarargy +# @date 2023-01-16 + +# Define the sources for the library +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/HardwareAdapter.cpp + ${SOURCES} + PARENT_SCOPE + ) \ No newline at end of file diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index f33a67b4..cfe86381 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -104,3 +104,7 @@ void *ExaGeoStatHardware::GetContext(common::Computation aComputation) const { } return nullptr; } + +ExaGeoStatHardware::ExaGeoStatHardware(const int &aCoreNumber, const int &aGpuNumber) { + +} From 6a61410641b226a7d4bc6445afefc694ccbf309f Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sat, 20 Jan 2024 21:17:58 +0200 Subject: [PATCH 14/82] try 2 gpu devices --- tests/heavy-tests/HeavyTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index 1bf702f0..821b25ed 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -134,7 +134,7 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp arguments_vector.push_back("--cores=" + to_string(cpu_size_distribution(gen))); // TODO: Till fixing cuda error with multiple devices. #ifdef USE_CUDA - arguments_vector.push_back("--gpus=1"); + arguments_vector.push_back("--gpus=2"); #endif arguments_vector.push_back("--kernel=" + aKernelName); From 346a0d882f4ef4250355ad9fdc056aa0b9c14b8a Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 22 Jan 2024 13:54:43 +0200 Subject: [PATCH 15/82] back to starsh --- cmake/ImportStarsH.cmake | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/cmake/ImportStarsH.cmake b/cmake/ImportStarsH.cmake index b7c842b6..2327bdc2 100644 --- a/cmake/ImportStarsH.cmake +++ b/cmake/ImportStarsH.cmake @@ -24,3 +24,52 @@ include(macros/ImportDependency) ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") + + +message("") +message("---------------------------------------- Stars-H AGAIN WITH OLD CMAKE") +find_package(STARSH) +if (NOT STARSH_FOUND) + + message("") + message(STATUS "Can't find STARSH using PkgConfig, Installing it through HiCMA") + include(ImportHiCMA) + if(NOT HiCMA_INSTALLED) + message(FATAL_ERROR "HiCMA Installation failed") + endif() + + message(STATUS "Now trying to find STARSH") +endif() +find_package(STARSH REQUIRED) +if (STARSH_FOUND) + include_directories(${STARSH_INCLUDE_DIRS_DEP}) + if (STARSH_LINKER_FLAGS) + list(APPEND CMAKE_EXE_LINKER_FLAGS "${STARSH_LINKER_FLAGS}") + endif () + if (STARSH_LIBRARY_DIRS) + # the RPATH to be used when installing + list(APPEND CMAKE_INSTALL_RPATH "${STARSH_LIBRARY_DIRS}") + endif () + if (STARSH_LIBRARIES) + # look for gsl + find_library(_STARSH_LIB NAME starsh PATHS ${STARSH_LIBRARY_DIRS}) + if (_STARSH_LIB AND NOT "${STARSH_LIBRARIES_DEP}" MATCHES "gsl") + execute_process(COMMAND nm ${_STARSH_LIB} COMMAND grep gsl RESULT_VARIABLE GSL_IN_STARSH) + if (${GSL_IN_STARSH} EQUAL 0) + message(STATUS "STARSH depends on gsl. Adding it to dependency list") + if (STARSH_LIBRARIES_DEP) + list(APPEND STARSH_LIBRARIES_DEP "gsl") + else () + list(APPEND STARSH_LIBRARIES "gsl") + endif () + endif () + endif () + # insert to dependencies + if (STARSH_LIBRARIES_DEP) + list(INSERT EXAGEOSTAT_DEP 0 ${STARSH_LIBRARIES_DEP}) + else () + list(INSERT EXAGEOSTAT_DEP 0 ${STARSH_LIBRARIES}) + endif () + endif () +endif() +message(STATUS "StarsH Done") \ No newline at end of file From 2d3bc171ab75a2985a3dafc99ecc60608d43bd8a Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 23 Jan 2024 00:30:48 +0200 Subject: [PATCH 16/82] a potential fix to doxygen --- CMakeLists.txt | 17 +++++----- cmake/ImportDoxygen.cmake | 33 +++++++++++++++++++ cmake/ImportStarsH.cmake | 49 ----------------------------- cmake/macros/ImportDependency.cmake | 3 ++ 4 files changed, 44 insertions(+), 58 deletions(-) create mode 100644 cmake/ImportDoxygen.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index b34b08c9..f4dfd407 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,6 @@ option(CREATE_PACKAGE "Enable a packaging system for distribution" OFF) # Cmake Module Paths set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") - # Select toolchain based on whether CUDA is enabled or not if (USE_CUDA) message("") @@ -48,7 +47,6 @@ endif () # Project Name and Version project(ExaGeoStatCPP VERSION 1.0.0 DESCRIPTION "ExaGeoStatCPP is a parallel high performance unified framework for geostatistics on manycore systems.") - # Show the current version of CMake. message(STATUS "CMAKE VERSION: ${CMAKE_VERSION}") # Enable C++ language @@ -61,8 +59,6 @@ add_definitions( -DKERNELS_PATH="${PROJECT_SOURCE_DIR}/inst/include/kernels/concrete/" ) - - # ExaGeoStatCPP depends on a CUDA # ------------------------------- if (USE_CUDA) @@ -87,6 +83,14 @@ if (USE_MPI) list(APPEND STARPU_COMPONENT_LIST "MPI") endif () +# Since doxygen is needed by many dependence in ExaGeoStatCPP, So it's required to be found. +include(ImportDoxygen) + +# ExaGeoStatCPP depends on a LAPACK/BLASPP +# ------------------------------- +include(ImportBLASPP) +include(ImportLapack) + # ExaGeoStatCPP depends on LAPACKE #----------------------------- message("") @@ -133,11 +137,6 @@ endif () # ------------------------------- include(ImportChameleon) -# ExaGeoStatCPP depends on a LAPACK/BLASPP -# ------------------------------- -include(ImportBLASPP) -include(ImportLapack) - # ExaGeoStatCPP Documentation if (BUILD_DOCS) find_package(Doxygen) diff --git a/cmake/ImportDoxygen.cmake b/cmake/ImportDoxygen.cmake new file mode 100644 index 00000000..5b10474b --- /dev/null +++ b/cmake/ImportDoxygen.cmake @@ -0,0 +1,33 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file ImportDoxygen.cmake +# @brief This script checks for Doxygen and installs it if it is not already installed using the ImportDependency method. +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2023-01-22 + + +# Set configurations for Doxygen +set(name "Doxygen") +set(tag "Release_1_9_1") # Use the tag corresponding to the desired version +set(version "1.9.1") +set(flag -G "Unix Makefiles") +set(url "https://github.com/doxygen/doxygen.git") +set(is_cmake ON) # Doxygen uses CMake +set(is_git ON) # We will be cloning from a git repository +set(auto_gen OFF) # Auto generation of project files is not needed + +if(EXISTS "${CMAKE_INSTALL_PREFIX}/DOXYGEN/bin/doxygen") + message("here") + set(DOXYGEN_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/DOXYGEN/bin/doxygen") +endif() + +# Include the ImportDependency script +include(macros/ImportDependency) +# Call ImportDependency to handle the fetching and building of Doxygen +ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) + +message(STATUS "${name} done") diff --git a/cmake/ImportStarsH.cmake b/cmake/ImportStarsH.cmake index 2327bdc2..b7c842b6 100644 --- a/cmake/ImportStarsH.cmake +++ b/cmake/ImportStarsH.cmake @@ -24,52 +24,3 @@ include(macros/ImportDependency) ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) message(STATUS "${name} done") - - -message("") -message("---------------------------------------- Stars-H AGAIN WITH OLD CMAKE") -find_package(STARSH) -if (NOT STARSH_FOUND) - - message("") - message(STATUS "Can't find STARSH using PkgConfig, Installing it through HiCMA") - include(ImportHiCMA) - if(NOT HiCMA_INSTALLED) - message(FATAL_ERROR "HiCMA Installation failed") - endif() - - message(STATUS "Now trying to find STARSH") -endif() -find_package(STARSH REQUIRED) -if (STARSH_FOUND) - include_directories(${STARSH_INCLUDE_DIRS_DEP}) - if (STARSH_LINKER_FLAGS) - list(APPEND CMAKE_EXE_LINKER_FLAGS "${STARSH_LINKER_FLAGS}") - endif () - if (STARSH_LIBRARY_DIRS) - # the RPATH to be used when installing - list(APPEND CMAKE_INSTALL_RPATH "${STARSH_LIBRARY_DIRS}") - endif () - if (STARSH_LIBRARIES) - # look for gsl - find_library(_STARSH_LIB NAME starsh PATHS ${STARSH_LIBRARY_DIRS}) - if (_STARSH_LIB AND NOT "${STARSH_LIBRARIES_DEP}" MATCHES "gsl") - execute_process(COMMAND nm ${_STARSH_LIB} COMMAND grep gsl RESULT_VARIABLE GSL_IN_STARSH) - if (${GSL_IN_STARSH} EQUAL 0) - message(STATUS "STARSH depends on gsl. Adding it to dependency list") - if (STARSH_LIBRARIES_DEP) - list(APPEND STARSH_LIBRARIES_DEP "gsl") - else () - list(APPEND STARSH_LIBRARIES "gsl") - endif () - endif () - endif () - # insert to dependencies - if (STARSH_LIBRARIES_DEP) - list(INSERT EXAGEOSTAT_DEP 0 ${STARSH_LIBRARIES_DEP}) - else () - list(INSERT EXAGEOSTAT_DEP 0 ${STARSH_LIBRARIES}) - endif () - endif () -endif() -message(STATUS "StarsH Done") \ No newline at end of file diff --git a/cmake/macros/ImportDependency.cmake b/cmake/macros/ImportDependency.cmake index c2de92f8..a66ddd22 100644 --- a/cmake/macros/ImportDependency.cmake +++ b/cmake/macros/ImportDependency.cmake @@ -53,6 +53,9 @@ macro(ImportDependency name tag version url flag components is_cmake is_git auto # If the package is not found, install it using BuildDependency message(" Can't find ${capital_name}, Installing it instead ..") BuildDependency(${name} ${url} ${tag} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) + if(${name} STREQUAL "Doxygen") + set(DOXYGEN_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/DOXYGEN/bin/doxygen") + endif() find_package(${name} ${version} REQUIRED COMPONENTS ${components}) endif () else () From 5446c7655f254917a477ace80859ea012a41c7fa Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 23 Jan 2024 11:50:39 +0200 Subject: [PATCH 17/82] roll back --- CMakeLists.txt | 17 ++++++++------- cmake/ImportDoxygen.cmake | 33 ----------------------------- cmake/macros/ImportDependency.cmake | 3 --- tests/heavy-tests/HeavyTests.cpp | 8 ++++++- 4 files changed, 16 insertions(+), 45 deletions(-) delete mode 100644 cmake/ImportDoxygen.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index f4dfd407..b34b08c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ option(CREATE_PACKAGE "Enable a packaging system for distribution" OFF) # Cmake Module Paths set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") + # Select toolchain based on whether CUDA is enabled or not if (USE_CUDA) message("") @@ -47,6 +48,7 @@ endif () # Project Name and Version project(ExaGeoStatCPP VERSION 1.0.0 DESCRIPTION "ExaGeoStatCPP is a parallel high performance unified framework for geostatistics on manycore systems.") + # Show the current version of CMake. message(STATUS "CMAKE VERSION: ${CMAKE_VERSION}") # Enable C++ language @@ -59,6 +61,8 @@ add_definitions( -DKERNELS_PATH="${PROJECT_SOURCE_DIR}/inst/include/kernels/concrete/" ) + + # ExaGeoStatCPP depends on a CUDA # ------------------------------- if (USE_CUDA) @@ -83,14 +87,6 @@ if (USE_MPI) list(APPEND STARPU_COMPONENT_LIST "MPI") endif () -# Since doxygen is needed by many dependence in ExaGeoStatCPP, So it's required to be found. -include(ImportDoxygen) - -# ExaGeoStatCPP depends on a LAPACK/BLASPP -# ------------------------------- -include(ImportBLASPP) -include(ImportLapack) - # ExaGeoStatCPP depends on LAPACKE #----------------------------- message("") @@ -137,6 +133,11 @@ endif () # ------------------------------- include(ImportChameleon) +# ExaGeoStatCPP depends on a LAPACK/BLASPP +# ------------------------------- +include(ImportBLASPP) +include(ImportLapack) + # ExaGeoStatCPP Documentation if (BUILD_DOCS) find_package(Doxygen) diff --git a/cmake/ImportDoxygen.cmake b/cmake/ImportDoxygen.cmake deleted file mode 100644 index 5b10474b..00000000 --- a/cmake/ImportDoxygen.cmake +++ /dev/null @@ -1,33 +0,0 @@ - -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# @file ImportDoxygen.cmake -# @brief This script checks for Doxygen and installs it if it is not already installed using the ImportDependency method. -# @version 1.0.1 -# @author Mahmoud ElKarargy -# @date 2023-01-22 - - -# Set configurations for Doxygen -set(name "Doxygen") -set(tag "Release_1_9_1") # Use the tag corresponding to the desired version -set(version "1.9.1") -set(flag -G "Unix Makefiles") -set(url "https://github.com/doxygen/doxygen.git") -set(is_cmake ON) # Doxygen uses CMake -set(is_git ON) # We will be cloning from a git repository -set(auto_gen OFF) # Auto generation of project files is not needed - -if(EXISTS "${CMAKE_INSTALL_PREFIX}/DOXYGEN/bin/doxygen") - message("here") - set(DOXYGEN_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/DOXYGEN/bin/doxygen") -endif() - -# Include the ImportDependency script -include(macros/ImportDependency) -# Call ImportDependency to handle the fetching and building of Doxygen -ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) - -message(STATUS "${name} done") diff --git a/cmake/macros/ImportDependency.cmake b/cmake/macros/ImportDependency.cmake index a66ddd22..c2de92f8 100644 --- a/cmake/macros/ImportDependency.cmake +++ b/cmake/macros/ImportDependency.cmake @@ -53,9 +53,6 @@ macro(ImportDependency name tag version url flag components is_cmake is_git auto # If the package is not found, install it using BuildDependency message(" Can't find ${capital_name}, Installing it instead ..") BuildDependency(${name} ${url} ${tag} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) - if(${name} STREQUAL "Doxygen") - set(DOXYGEN_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/DOXYGEN/bin/doxygen") - endif() find_package(${name} ${version} REQUIRED COMPONENTS ${components}) endif () else () diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index 821b25ed..e4bcf929 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -14,6 +14,9 @@ #include #include #include +#ifdef USE_CUDA +#include +#endif #include @@ -134,7 +137,10 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp arguments_vector.push_back("--cores=" + to_string(cpu_size_distribution(gen))); // TODO: Till fixing cuda error with multiple devices. #ifdef USE_CUDA - arguments_vector.push_back("--gpus=2"); + int nDevices; + cudaGetDeviceCount(&nDevices); + uniform_int_distribution gpu_size_distribution(1, nDevices); + arguments_vector.push_back("--gpus=" + to_string(gpu_size_distribution(gen))); #endif arguments_vector.push_back("--kernel=" + aKernelName); From da8ad1fab730e82aa0fa9e0f3b74c0a086407ef4 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 4 Feb 2024 19:05:42 +0200 Subject: [PATCH 18/82] test Rcpp --- .gitignore | 1 + CMakeLists.txt | 14 +- ExaGeoStatCPPConfig.cmake.in | 2 +- R/ExaGeoStatCPP.R | 3 +- USER_MANUAL.md | 4 +- clean_build.sh | 6 +- .../FindPkgconfigLibrariesAbsolutePath.cmake | 2 +- cmake/FindR.cmake | 1 - cmake/ImportBLAS.cmake | 2 +- cmake/ImportBLASPP.cmake | 35 +- cmake/ImportCatch2.cmake | 2 +- cmake/ImportChameleon.cmake | 2 +- cmake/ImportGSL.cmake | 2 +- cmake/ImportHCore.cmake | 2 +- cmake/ImportHiCMA.cmake | 2 +- cmake/ImportHwloc.cmake | 2 +- cmake/ImportLapack.cmake | 2 +- cmake/ImportNLOPT.cmake | 2 +- cmake/ImportStarPu.cmake | 2 +- cmake/ImportStarsH.cmake | 2 +- cmake/macros/BuildDependency.cmake | 5 +- cmake/macros/ImportDependency.cmake | 3 +- cmake/toolchains/CudaToolchain.cmake | 2 +- cmake/toolchains/GccToolchain.cmake | 2 +- config.sh | 165 ------ configure | 176 +++++- docs/CMakeLists.txt | 2 +- examples/CMakeLists.txt | 2 +- examples/configurations/CMakeLists.txt | 2 +- .../configurations/ConfigurationModule.cpp | 7 +- examples/data-generators/CMakeLists.txt | 2 +- .../SyntheticDataGeneration.cpp | 6 +- examples/end-to-end/CMakeLists.txt | 2 +- examples/end-to-end/DataGeneration.cpp | 10 +- .../end-to-end/DataGenerationAndModeling.cpp | 10 +- .../DataGenerationModelingAndPrediction.cpp | 10 +- examples/end-to-end/DataModeling.cpp | 8 +- examples/end-to-end/DataPrediction.cpp | 8 +- .../Rcpp-adapters/FunctionsAdapter.hpp | 75 +++ inst/include/api/ExaGeoStat.hpp | 16 +- inst/include/common/Definitions.hpp | 2 +- inst/include/common/PluginRegistry.hpp | 2 +- inst/include/common/Utils.hpp | 106 ---- .../include/configurations/Configurations.hpp | 546 +++++++++--------- .../include/data-generators/DataGenerator.hpp | 10 +- .../concrete/CSVDataGenerator.hpp | 8 +- .../concrete/SyntheticGenerator.hpp | 6 +- inst/include/data-units/DescriptorData.hpp | 2 +- inst/include/data-units/ExaGeoStatData.hpp | 11 +- inst/include/data-units/Locations.hpp | 2 +- .../data-units/ModelingDataHolders.hpp | 12 +- .../descriptor/ExaGeoStatDescriptor.hpp | 2 +- .../concrete/ChameleonDescriptor.hpp | 2 +- .../descriptor/concrete/HicmaDescriptor.hpp | 2 +- inst/include/hardware/ExaGeoStatHardware.hpp | 104 ++-- inst/include/helpers/CommunicatorMPI.hpp | 2 +- inst/include/helpers/DiskWriter.hpp | 2 +- .../helpers/DistanceCalculationHelpers.hpp | 2 +- inst/include/kernels/Kernel.hpp | 2 +- .../concrete/BivariateMaternFlexible.hpp | 2 +- .../concrete/BivariateMaternParsimonious.hpp | 2 +- .../BivariateSpacetimeMaternStationary.hpp | 2 +- .../concrete/TrivariateMaternParsimonious.hpp | 2 +- .../concrete/UnivariateExpNonGaussian.hpp | 2 +- .../concrete/UnivariateMaternDbeta.hpp | 2 +- .../concrete/UnivariateMaternDdbetaBeta.hpp | 2 +- .../concrete/UnivariateMaternDdbetaNu.hpp | 2 +- .../concrete/UnivariateMaternDdnuNu.hpp | 2 +- .../UnivariateMaternDdsigmaSquare.hpp | 2 +- .../UnivariateMaternDdsigmaSquareBeta.hpp | 2 +- .../UnivariateMaternDdsigmaSquareNu.hpp | 2 +- .../kernels/concrete/UnivariateMaternDnu.hpp | 2 +- .../concrete/UnivariateMaternDsigmaSquare.hpp | 2 +- .../concrete/UnivariateMaternNonGaussian.hpp | 2 +- .../UnivariateMaternNuggetsStationary.hpp | 2 +- .../concrete/UnivariateMaternStationary.hpp | 2 +- .../UnivariateSpacetimeMaternStationary.hpp | 2 +- .../LinearAlgebraFactory.hpp | 2 +- .../LinearAlgebraMethods.hpp | 40 +- .../concrete/ChameleonHeaders.hpp | 2 +- .../concrete/HicmaHeaders.hpp | 2 +- .../chameleon/ChameleonImplementation.hpp | 6 +- .../hicma/tlr/HicmaImplementation.hpp | 6 +- inst/include/prediction/Prediction.hpp | 8 +- .../PredictionAuxiliaryFunctions.hpp | 2 +- inst/include/prediction/PredictionHelpers.hpp | 4 +- inst/include/results/Results.hpp | 2 +- inst/include/utilities/EnumStringParser.hpp | 66 +++ inst/include/utilities/ErrorHandler.hpp | 131 +++++ inst/include/utilities/Logger.hpp | 132 +++++ inst/include/utilities/Printer.hpp | 38 ++ scripts/Benchmarking.sh | 2 +- src/CMakeLists.txt | 12 +- src/DataTypeModule.cpp | 29 - .../CMakeLists.txt | 8 +- src/Rcpp-adapters/FunctionsAdapter.cpp | 120 ++++ src/Rcpp-adapters/RcppExports.cpp | 45 ++ src/Rcpp-adapters/RcppModules.cpp | 51 ++ src/RcppExports.cpp | 30 - src/api/CMakeLists.txt | 2 +- src/api/ExaGeoStat.cpp | 10 +- src/configurations/CMakeLists.txt | 2 +- src/configurations/Configurations.cpp | 30 +- src/data-generators/CMakeLists.txt | 2 +- src/data-generators/DataGenerator.cpp | 16 +- src/data-generators/concrete/CMakeLists.txt | 2 +- .../concrete/CSVDataGenerator.cpp | 10 +- .../concrete/SyntheticGenerator.cpp | 12 +- src/data-units/CMakeLists.txt | 2 +- src/data-units/DescriptorData.cpp | 2 +- src/data-units/ExaGeoStatData.cpp | 20 +- src/data-units/Locations.cpp | 2 +- src/data-units/descriptor/CMakeLists.txt | 2 +- .../descriptor/ExaGeoStatDescriptor.cpp | 2 +- .../descriptor/concrete/CMakeLists.txt | 2 +- .../concrete/ChameleonDescriptor.cpp | 2 +- .../descriptor/concrete/HicmaDescriptor.cpp | 2 +- src/hardware/CMakeLists.txt | 2 +- src/hardware/ExaGeoStatHardware.cpp | 58 +- src/helpers/CMakeLists.txt | 2 +- src/helpers/CommunicatorMPI.cpp | 2 +- src/helpers/DiskWriter.cpp | 2 +- src/helpers/DistanceCalculationHelpers.cpp | 2 +- src/kernels/CMakeLists.txt | 2 +- src/kernels/Kernel.cpp | 2 +- .../concrete/BivariateMaternFlexible.cpp | 2 +- .../concrete/BivariateMaternParsimonious.cpp | 2 +- .../BivariateSpacetimeMaternStationary.cpp | 2 +- .../concrete/TrivariateMaternParsimonious.cpp | 2 +- .../concrete/UnivariateExpNonGaussian.cpp | 2 +- .../concrete/UnivariateMaternDbeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaBeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaNu.cpp | 2 +- .../concrete/UnivariateMaternDdnuNu.cpp | 2 +- .../UnivariateMaternDdsigmaSquare.cpp | 2 +- .../UnivariateMaternDdsigmaSquareBeta.cpp | 2 +- .../UnivariateMaternDdsigmaSquareNu.cpp | 2 +- src/kernels/concrete/UnivariateMaternDnu.cpp | 2 +- .../concrete/UnivariateMaternDsigmaSquare.cpp | 2 +- .../concrete/UnivariateMaternNonGaussian.cpp | 2 +- .../UnivariateMaternNuggetsStationary.cpp | 2 +- .../concrete/UnivariateMaternStationary.cpp | 2 +- .../UnivariateSpacetimeMaternStationary.cpp | 2 +- src/linear-algebra-solvers/CMakeLists.txt | 2 +- .../LinearAlgebraFactory.cpp | 1 - .../LinearAlgebraMethods.cpp | 36 +- .../chameleon/ChameleonImplementation.cpp | 7 +- .../concrete/tlr/HicmaImplementation.cpp | 10 +- src/prediction/CMakeLists.txt | 2 +- src/prediction/Prediction.cpp | 7 +- .../PredictionAuxiliaryFunctions.cpp | 4 +- src/prediction/PredictionHelpers.cpp | 5 +- src/results/CMakeLists.txt | 2 +- src/results/Results.cpp | 12 +- tests/cpp-tests/api/CMakeLists.txt | 2 +- tests/cpp-tests/api/TestExaGeoStatApi.cpp | 14 +- .../configurations/TestConfigurations.cpp | 3 +- .../cpp-tests/data-generators/CMakeLists.txt | 2 +- .../concrete/TestCSVDataGenerator.cpp | 12 +- .../concrete/TestSyntheticGenerator.cpp | 7 +- tests/cpp-tests/helpers/CMakeLists.txt | 2 +- .../helpers/TestPredictionHelpers.cpp | 2 +- tests/cpp-tests/kernels/CMakeLists.txt | 2 +- .../concrete/TestBivariateMaternFlexible.cpp | 6 +- .../TestBivariateMaternParsimonious.cpp | 6 +- ...TestBivariateSpacetimeMaternStationary.cpp | 6 +- .../TestTrivariateMaternParsimonious.cpp | 6 +- .../concrete/TestUnivariateExpNonGaussian.cpp | 2 +- .../concrete/TestUnivariateMaternDbeta.cpp | 4 +- .../TestUnivariateMaternDdbetaBeta.cpp | 3 +- .../concrete/TestUnivariateMaternDdbetaNu.cpp | 4 +- .../concrete/TestUnivariateMaternDdnuNu.cpp | 3 +- .../TestUnivariateMaternDdsigmaSquare.cpp | 7 +- .../TestUnivariateMaternDdsigmaSquareBeta.cpp | 4 +- .../TestUnivariateMaternDdsigmaSquareNu.cpp | 3 +- .../concrete/TestUnivariateMaternDnu.cpp | 3 +- .../TestUnivariateMaternDsigmaSquare.cpp | 3 +- .../TestUnivariateMaternNonGaussian.cpp | 6 +- .../TestUnivariateMaternNuggetsStationary.cpp | 6 +- .../TestUnivariateMaternStationary.cpp | 6 +- ...estUnivariateSpacetimeMaternStationary.cpp | 6 +- .../linear-algebra-solvers/CMakeLists.txt | 2 +- .../TestChameleonImplementationDST.cpp | 4 +- .../TestChameleonImplementationDense.cpp | 4 +- .../concrete/TestHiCMAImplementationTLR.cpp | 8 +- tests/heavy-tests/CMakeLists.txt | 2 +- tests/heavy-tests/ExamplesTests.cpp | 3 +- tests/heavy-tests/HeavyTests.cpp | 3 +- 188 files changed, 1549 insertions(+), 1127 deletions(-) delete mode 100755 config.sh create mode 100644 inst/include/Rcpp-adapters/FunctionsAdapter.hpp delete mode 100644 inst/include/common/Utils.hpp create mode 100644 inst/include/utilities/EnumStringParser.hpp create mode 100644 inst/include/utilities/ErrorHandler.hpp create mode 100644 inst/include/utilities/Logger.hpp create mode 100644 inst/include/utilities/Printer.hpp delete mode 100644 src/DataTypeModule.cpp rename src/{adapters => Rcpp-adapters}/CMakeLists.txt (65%) create mode 100644 src/Rcpp-adapters/FunctionsAdapter.cpp create mode 100644 src/Rcpp-adapters/RcppExports.cpp create mode 100644 src/Rcpp-adapters/RcppModules.cpp delete mode 100644 src/RcppExports.cpp diff --git a/.gitignore b/.gitignore index 68debb78..4ab21a78 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ installdir/ # R files .Rhistory .RData +.RDataTmp # tar.gz files *.tar.gz diff --git a/CMakeLists.txt b/CMakeLists.txt index b8e177ec..f75fc822 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ # @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah -# @date 2023-01-30 +# @date 2024-02-04 # Set the minimum CMake version required to 3.20 cmake_minimum_required(VERSION 3.20 FATAL_ERROR) @@ -30,6 +30,12 @@ option(CREATE_PACKAGE "Enable a packaging system for distribution" OFF) # Cmake Module Paths set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") +if (${BUILD_SHARED_LIBS}) + set(BLA_STATIC OFF) +else () + set(BLA_STATIC ON) +endif () + # Select toolchain based on whether CUDA is enabled or not if (USE_CUDA) message("") @@ -155,12 +161,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prerequisites) set(MY_LOGGER_PATH ${CMAKE_CURRENT_SOURCE_DIR}) add_definitions(-DMY_LOGGER_PATH="${CMAKE_CURRENT_SOURCE_DIR}") -if (${BUILD_SHARED_LIBS}) - set(BLA_STATIC OFF) -else () - set(BLA_STATIC ON) -endif () - if (USE_R) message("") message("---------------------------------------- Rcpp") diff --git a/ExaGeoStatCPPConfig.cmake.in b/ExaGeoStatCPPConfig.cmake.in index ef8a1c2c..4e7ec663 100644 --- a/ExaGeoStatCPPConfig.cmake.in +++ b/ExaGeoStatCPPConfig.cmake.in @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file ExaGeoStatCPPConfig.cmake.in -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-30 diff --git a/R/ExaGeoStatCPP.R b/R/ExaGeoStatCPP.R index 969f73a2..c2ac7275 100755 --- a/R/ExaGeoStatCPP.R +++ b/R/ExaGeoStatCPP.R @@ -1,2 +1 @@ - - loadModule("ExaGeoStatCPP", TRUE) +loadModule("ExaGeoStatCPP", TRUE) diff --git a/USER_MANUAL.md b/USER_MANUAL.md index e9d1e456..7f95fd8e 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -242,7 +242,7 @@ can be used to train the software to predict the values of new data better. After you provide your arguments with the Configurations module, you must do the following two steps: ```c++ // Create a new ExaGeoStat data that holds the locations and descriptors data. -std::unique_ptr> data; +std::unique_ptr> data; // Generate data by passing your arguments through the configurations, hardware, and container of the data, which will be filled with the newly generated data. ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); @@ -256,7 +256,7 @@ ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); - Then do the following two steps. ```c++ // Create a new ExaGeoStat data that holds the locations data and descriptors data. -std::unique_ptr> data; +std::unique_ptr> data; // Generate data by passing your arguments through the configurations, your hardware and your container of the data which will be filled with the new generated data. ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); ``` diff --git a/clean_build.sh b/clean_build.sh index d1f1a1d2..7f554073 100755 --- a/clean_build.sh +++ b/clean_build.sh @@ -1,11 +1,11 @@ -#!/bin/bash +#! /bin/sh # Copyright (c) 2017-2023 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file clean_build.sh # @brief This script cleans and builds a software package called ExaGeoStat. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-30 @@ -55,7 +55,7 @@ cd bin/ || { } # Clean the directory and build the code with the specified options. -cmake --build . $num_proc $verbose +cmake --build . "$num_proc" $verbose # Install the software if the -i option is provided. if [ "$installation" -eq 1 ]; then diff --git a/cmake/FindPkgconfigLibrariesAbsolutePath.cmake b/cmake/FindPkgconfigLibrariesAbsolutePath.cmake index c1c94c1f..5eef4e0b 100644 --- a/cmake/FindPkgconfigLibrariesAbsolutePath.cmake +++ b/cmake/FindPkgconfigLibrariesAbsolutePath.cmake @@ -16,7 +16,7 @@ # Univ. of California Berkeley, # Univ. of Colorado Denver. # -# @version 1.0.0 +# @version 1.1.0 # @author Florent Pruvost # @date 06-04-2018 # diff --git a/cmake/FindR.cmake b/cmake/FindR.cmake index f095bbf2..c0cde2c9 100644 --- a/cmake/FindR.cmake +++ b/cmake/FindR.cmake @@ -167,7 +167,6 @@ set_target_properties(R IMPORTED_LOCATION ${RCPP_LIB} ) -message("${RCPP_INCLUDE_DIRS}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(R DEFAULT_MSG R_INCLUDE R_LIBRARIES) diff --git a/cmake/ImportBLAS.cmake b/cmake/ImportBLAS.cmake index 72e6d460..28a9c901 100644 --- a/cmake/ImportBLAS.cmake +++ b/cmake/ImportBLAS.cmake @@ -4,7 +4,7 @@ # @file ImportBLAS.cmake # @brief This file searches for the BLAS library and includes it if not already included. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-03-12 diff --git a/cmake/ImportBLASPP.cmake b/cmake/ImportBLASPP.cmake index 1b2f7e29..ef008506 100644 --- a/cmake/ImportBLASPP.cmake +++ b/cmake/ImportBLASPP.cmake @@ -4,21 +4,44 @@ # @file ImportBLASPP.cmake # @brief This file searches for the BLAS++ library and includes it if not already included. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy -# @date 2023-03-12 +# @date 2024-02-04 include(ImportBLAS) #Configurations set(name blaspp) +string(TOUPPER ${name} capital_name) set(tag "v2023.01.00") set(url "https://github.com/icl-utk-edu/blaspp") -set(${name}_DIR "${CMAKE_INSTALL_PREFIX}/${capital_name}/lib/cmake/${name}") +set(${name}_DIR "${CMAKE_INSTALL_PREFIX}/${capital_name}/${name}-build/") -include(FetchContent) -FetchContent_Declare(${name} GIT_REPOSITORY "${url}" GIT_TAG "${tag}") -FetchContent_MakeAvailable(${name}) +message("") +message("---------------------------------------- ${capital_name}") +message(STATUS "Checking for ${capital_name} with Version ${version}") + +# Check if the target is already included +IF (NOT TARGET ${name}) + include(FindPkgConfig) + find_package(PkgConfig QUIET) + find_package(${name} ${version} QUIET COMPONENTS ${components}) + + # If the package is found, print a message + if (${name}_FOUND) + message(" Found ${capital_name}; ${${name}_DIR} ${${name}_LIBRARIES}") + else () + # If the package is not found, install it using BuildDependency + message(" Can't find ${capital_name}, Installing it instead ..") + include(FetchContent) + set(FETCHCONTENT_BASE_DIR ${CMAKE_INSTALL_PREFIX}/${capital_name}) + FetchContent_Declare(${name} GIT_REPOSITORY "${url}" GIT_TAG "${tag}") + FetchContent_MakeAvailable(${name}) + + endif () +else () + message(STATUS "${capital_name} already included") +endif () set(LIBS ${name} ${LIBS}) message(STATUS "${name} done") \ No newline at end of file diff --git a/cmake/ImportCatch2.cmake b/cmake/ImportCatch2.cmake index 1d255902..c00a6e29 100644 --- a/cmake/ImportCatch2.cmake +++ b/cmake/ImportCatch2.cmake @@ -5,7 +5,7 @@ # @file ImportChameleon.cmake # @brief This script checks for Chameleon and includes it in the project if it is not already a target. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-03-13 diff --git a/cmake/ImportChameleon.cmake b/cmake/ImportChameleon.cmake index a71ae88c..b1d47819 100644 --- a/cmake/ImportChameleon.cmake +++ b/cmake/ImportChameleon.cmake @@ -5,7 +5,7 @@ # @file ImportChameleon.cmake # @brief This script checks for Chameleon and includes it in the project if it is not already a target. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 diff --git a/cmake/ImportGSL.cmake b/cmake/ImportGSL.cmake index f6ebd0ff..a4010bab 100644 --- a/cmake/ImportGSL.cmake +++ b/cmake/ImportGSL.cmake @@ -5,7 +5,7 @@ # @file ImportGSL.cmake # @brief Checks for the GSL library and includes it in the project if it is not already present. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-16 diff --git a/cmake/ImportHCore.cmake b/cmake/ImportHCore.cmake index 5f1c53cf..42f26fa5 100644 --- a/cmake/ImportHCore.cmake +++ b/cmake/ImportHCore.cmake @@ -5,7 +5,7 @@ # @file ImportHCore.cmake # @brief Checks for the Hcore library and includes it in the project if it is not already present. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-15 diff --git a/cmake/ImportHiCMA.cmake b/cmake/ImportHiCMA.cmake index 7257ee15..e8b5e2ef 100644 --- a/cmake/ImportHiCMA.cmake +++ b/cmake/ImportHiCMA.cmake @@ -5,7 +5,7 @@ # @file ImportHiCMA.cmake # @brief Find and include HiCMA library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 diff --git a/cmake/ImportHwloc.cmake b/cmake/ImportHwloc.cmake index a4f71525..c0f22bbc 100644 --- a/cmake/ImportHwloc.cmake +++ b/cmake/ImportHwloc.cmake @@ -5,7 +5,7 @@ # @file ImportHwloc.cmake # @brief Find and include Hwloc library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-15 diff --git a/cmake/ImportLapack.cmake b/cmake/ImportLapack.cmake index 5a6a6c64..50586cd0 100644 --- a/cmake/ImportLapack.cmake +++ b/cmake/ImportLapack.cmake @@ -5,7 +5,7 @@ # @file ImportLapack.cmake # @brief Find and include LAPACK library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-12 diff --git a/cmake/ImportNLOPT.cmake b/cmake/ImportNLOPT.cmake index ac796b44..c99add7f 100644 --- a/cmake/ImportNLOPT.cmake +++ b/cmake/ImportNLOPT.cmake @@ -5,7 +5,7 @@ # @file ImportNLOPT.cmake # @brief Find and include NLOPT library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-26 diff --git a/cmake/ImportStarPu.cmake b/cmake/ImportStarPu.cmake index b160c574..24dbb773 100644 --- a/cmake/ImportStarPu.cmake +++ b/cmake/ImportStarPu.cmake @@ -5,7 +5,7 @@ # @file ImportSTARPU.cmake # @brief Find and include STARPU library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 diff --git a/cmake/ImportStarsH.cmake b/cmake/ImportStarsH.cmake index b7c842b6..c0c1ce93 100644 --- a/cmake/ImportStarsH.cmake +++ b/cmake/ImportStarsH.cmake @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief Find and include STARSH library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 diff --git a/cmake/macros/BuildDependency.cmake b/cmake/macros/BuildDependency.cmake index 90375b4d..bc112db9 100644 --- a/cmake/macros/BuildDependency.cmake +++ b/cmake/macros/BuildDependency.cmake @@ -4,9 +4,10 @@ # @file BuildDependency.cmake # @brief Fetches, builds, and installs a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy -# @date 2023-03-12 +# @author Amr Nasr +# @date 2024-02-04 # After building and installing the dependency, the macro installs the lib, include, and share directories # in the current directory. diff --git a/cmake/macros/ImportDependency.cmake b/cmake/macros/ImportDependency.cmake index c2de92f8..5306bf4a 100644 --- a/cmake/macros/ImportDependency.cmake +++ b/cmake/macros/ImportDependency.cmake @@ -4,8 +4,9 @@ # @file ImportDependency.cmake # @brief CMake script for importing and building external dependencies. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy +# @author Amr Nasr # @date 2023-12-28 # ImportDependency Macro: diff --git a/cmake/toolchains/CudaToolchain.cmake b/cmake/toolchains/CudaToolchain.cmake index aaf7ae59..a84a2666 100644 --- a/cmake/toolchains/CudaToolchain.cmake +++ b/cmake/toolchains/CudaToolchain.cmake @@ -5,7 +5,7 @@ # @file CudaToolchain.cmake # @brief This file is used to set up the CUDA toolchain for compilation. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-12 diff --git a/cmake/toolchains/GccToolchain.cmake b/cmake/toolchains/GccToolchain.cmake index d02499de..3fda7f79 100644 --- a/cmake/toolchains/GccToolchain.cmake +++ b/cmake/toolchains/GccToolchain.cmake @@ -4,7 +4,7 @@ # @file GccToolchain.cmake # @brief This file is used to set up the GCC toolchain for compilation. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-12 diff --git a/config.sh b/config.sh deleted file mode 100755 index e00b7979..00000000 --- a/config.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/bin/bash -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# @file config.sh -# @version 1.1.0 -# @author Mahmoud ElKarargy -# @date 2023-01-30 - -# Set variables and default values -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -INSTALL_PREFIX=$PWD/installdir/_deps -PROJECT_SOURCE_DIR=$(dirname "$0") -BUILDING_TESTS="OFF" -BUILDING_HEAVY_TESTS="OFF" -BUILDING_EXAMPLES="OFF" -USING_HiCMA="OFF" -VERBOSE="OFF" -USE_CUDA="OFF" -USE_MPI="OFF" -BLAS_VENDOR="" -PACKAGE="OFF" -SHOW_WARNINGS="OFF" -COMPILE_FLAGS="-Wl,--no-as-needed" -DEVELOPER_WARNINGS="-Wno-dev" - -# Parse command line options -while getopts ":tevhHi:cmspTw" opt; do - case $opt in - i) ##### Define installation path ##### - echo -e "${YELLOW}Installation path set to $OPTARG.${NC}" - INSTALL_PREFIX=$OPTARG - ;; - t) ##### Building tests enabled ##### - echo -e "${GREEN}Building tests enabled.${NC}" - BUILDING_TESTS="ON" - ;; - T) ##### Building heavy tests enabled ##### - echo -e "${GREEN}Building heavy tests enabled.${NC}" - BUILDING_HEAVY_TESTS="ON" - ;; - e) ##### Building examples enabled ##### - echo -e "${GREEN}Building examples enabled.${NC}" - BUILDING_EXAMPLES="ON" - ;; - H) ##### Using HiCMA ##### - echo -e "${GREEN}Using HiCMA.${NC}" - USING_HiCMA="ON" - ;; - c)##### Using cuda enabled ##### - echo -e "${GREEN}Cuda enabled ${NC}" - USE_CUDA="ON" - ;; - m)##### Using MPI enabled ##### - echo -e "${GREEN}MPI enabled ${NC}" - USE_MPI="ON" - ;; - v) ##### printing full output of make ##### - echo -e "${GREEN}printing make with details.${NC}" - VERBOSE="ON" - ;; - s) ##### Passing BLA vendor with mkl ##### - echo -e "${GREEN}MKL as a BLA vendor${NC}" - BLAS_VENDOR="Intel10_64lp" - ;; - p) ##### Enabling packaging system for distribution ##### - echo -e "${GREEN}CPACK enabled${NC}" - PACKAGE=ON - ;; - w) ##### Enable showing all the warnings ##### - echo -e "${GREEN}Showing Warnings is enabled${NC}" - SHOW_WARNINGS="ON" - ;; - \?) ##### Error unknown option ##### - echo "Option $OPTARG parameter is unknown, please -h for help" - exit 1 - ;; - :) ##### Error in an option ##### - echo "Option $OPTARG requires parameter(s)" - exit 0 - ;; - h) ##### Prints the help ##### - echo "Usage of $(basename "$0"):" - echo "" - printf "%20s %s\n" "-i [path] :" "specify installation path, default = ${PWD}/installdir/_deps/" - printf "%20s %s\n" "-t :" "to enable building tests." - printf "%20s %s\n" "-T :" "to enable building heavy tests." - printf "%20s %s\n" "-e :" "to enable building examples." - printf "%20s %s\n" "-H :" "to enable using HiCMA." - printf "%20s %s\n" "-c :" "to enable using CUDA." - printf "%20s %s\n" "-m :" "to enable using MPI." - printf "%20s %s\n" "-v :" "to enable verbose printings." - printf "%20s %s\n" "-d :" "to enable debug mode." - printf "%20s %s\n" "-s :" "to manually pass MKL as your bla vendor." - printf "%20s %s\n" "-p :" "to enable a packaging system for distribution." - printf "%20s %s\n" "-h :" "Help." - echo "" - exit 1 - ;; - esac -done - -if [ -z "$BUILDING_TESTS" ]; then - BUILDING_TESTS="OFF" - echo -e "${RED}Building tests disabled.${NC}" -fi - -if [ -z "$BUILDING_EXAMPLES" ]; then - BUILDING_EXAMPLES="OFF" - echo -e "${RED}Building examples disabled.${NC}" -fi - -echo -e "${BLUE}Installation path set to $INSTALL_PREFIX.${NC}" - -if [ -z "$USING_HiCMA" ]; then - echo -e "${RED}Using HiCMA is disabled.${NC}" -fi - -if [ -z "$USE_CUDA" ]; then - USE_CUDA="OFF" - echo -e "${RED}Using CUDA disabled${NC}" -fi - -if [ -z "$USE_MPI" ]; then - USE_MPI="OFF" - echo -e "${RED}Using MPI disabled${NC}" -fi - -if [ "$SHOW_WARNINGS" = "ON" ]; then - COMPILE_FLAGS+=" -W" - DEVELOPER_WARNINGS="" -elif [ "$SHOW_WARNINGS" = "OFF" ]; then - COMPILE_FLAGS+=" -w" -fi - -echo "" -echo -e "${YELLOW}Use -h to print the usages of exageostat-cpp flags.${NC}" -echo "" -rm -rf bin/ -mkdir -p bin/ - -cmake $DEVELOPER_WARNINGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DCMAKE_BUILD_TYPE=RELEASE \ - -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ - -DBUILD_TESTS="${BUILDING_TESTS}" \ - -DBUILD_HEAVY_TESTS="${BUILDING_HEAVY_TESTS}" \ - -DBUILD_EXAMPLES="${BUILDING_EXAMPLES}" \ - -DUSE_HICMA="${USING_HiCMA}" \ - -DCMAKE_VERBOSE_MAKEFILE:BOOL=${VERBOSE} \ - -DUSE_CUDA="${USE_CUDA}" \ - -DUSE_MPI="${USE_MPI}" \ - -DBLA_VENDOR="${BLAS_VENDOR}" \ - -DCREATE_PACKAGE="${PACKAGE}" \ - -DBUILD_SHARED_LIBS=OFF \ - -H"${PROJECT_SOURCE_DIR}" \ - -B"${PROJECT_SOURCE_DIR}/bin" \ - -G "Unix Makefiles" \ - -DCMAKE_CXX_FLAGS_DEBUG="$COMPILE_FLAGS"\ - -DCMAKE_CXX_FLAGS_RELEASE="$COMPILE_FLAGS" diff --git a/configure b/configure index f1e8faba..7abb25de 100755 --- a/configure +++ b/configure @@ -1,20 +1,30 @@ -#! /bin/bash +#! /bin/sh # Copyright (c) 2017-2023 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file config.sh # @version 1.1.0 -# @author David Helmy # @author Mahmoud ElKarargy -# @date 2024-01-15 +# @author David Helmy +# @date 2024-02-04 + +# Set variables and default values +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +NC='\033[0m' +# shellcheck disable=SC2164 cd "$(dirname "$0")" +# Get the operating system type using uname +OS_TYPE=$(uname) -if [[ "$OSTYPE" == "darwin"* ]]; then +if [ "$OS_TYPE" = "darwin"* ]; then ABSOLUTE_PATH=$([[ $1 == /* ]] && echo "$1" || echo "$PWD/${1#./}") else - ABSOLUTE_PATH=$(dirname $(realpath "$0")) + ABSOLUTE_PATH=$(dirname "$(realpath "$0")") fi INSTALL_PREFIX=$PWD/installdir/_deps @@ -30,10 +40,133 @@ PACKAGE="OFF" COMPILE_FLAGS="-Wl,--no-as-needed -w -fpic" DEVELOPER_WARNINGS="-Wno-dev" +# Parse command line options +while getopts ":tevhHi:cmspTwr" opt; do + case $opt in + i) ##### Define installation path ##### + echo "${YELLOW}Installation path set to $OPTARG.${NC}" + INSTALL_PREFIX=$OPTARG + ;; + t) ##### Building tests enabled ##### + echo "${GREEN}Building tests enabled.${NC}" + BUILDING_TESTS="ON" + ;; + T) ##### Building heavy tests enabled ##### + echo "${GREEN}Building heavy tests enabled.${NC}" + BUILDING_HEAVY_TESTS="ON" + ;; + e) ##### Building examples enabled ##### + echo "${GREEN}Building examples enabled.${NC}" + BUILDING_EXAMPLES="ON" + ;; + H) ##### Using HiCMA ##### + echo "${GREEN}Using HiCMA.${NC}" + USING_HiCMA="ON" + ;; + c) ##### Using cuda enabled ##### + echo "${GREEN}Cuda enabled ${NC}" + USE_CUDA="ON" + ;; + m) ##### Using MPI enabled ##### + echo "${GREEN}MPI enabled ${NC}" + USE_MPI="ON" + ;; + v) ##### printing full output of make ##### + echo "${GREEN}printing make with details.${NC}" + VERBOSE="ON" + ;; + s) ##### Passing BLA vendor with mkl ##### + echo "${GREEN}MKL as a BLA vendor${NC}" + BLAS_VENDOR="Intel10_64lp" + ;; + p) ##### Enabling packaging system for distribution ##### + echo "${GREEN}CPACK enabled${NC}" + PACKAGE=ON + ;; + w) ##### Enable showing all the warnings ##### + echo "${GREEN}Showing Warnings is enabled${NC}" + SHOW_WARNINGS="ON" + ;; + r) ##### Enable R and Rcpp support ##### + echo "${GREEN}R is enabled${NC}" + USE_R="ON" + ;; + \?) ##### Error unknown option ##### + echo "Option $OPTARG parameter is unknown, please -h for help" + exit 1 + ;; + :) ##### Error in an option ##### + echo "Option $OPTARG requires parameter(s)" + exit 0 + ;; + h) ##### Prints the help ##### + echo "Usage of $(basename "$0"):" + echo "" + printf "%20s %s\n" "-i [path] :" "specify installation path, default = ${PWD}/installdir/_deps/" + printf "%20s %s\n" "-t :" "to enable building tests." + printf "%20s %s\n" "-T :" "to enable building heavy tests." + printf "%20s %s\n" "-e :" "to enable building examples." + printf "%20s %s\n" "-H :" "to enable using HiCMA." + printf "%20s %s\n" "-c :" "to enable using CUDA." + printf "%20s %s\n" "-m :" "to enable using MPI." + printf "%20s %s\n" "-v :" "to enable verbose printings." + printf "%20s %s\n" "-d :" "to enable debug mode." + printf "%20s %s\n" "-s :" "to manually pass MKL as your bla vendor." + printf "%20s %s\n" "-p :" "to enable a packaging system for distribution." + printf "%20s %s\n" "-h :" "Help." + echo "" + exit 1 + ;; + esac +done + +if [ -z "$BUILDING_TESTS" ]; then + BUILDING_TESTS="OFF" + echo "${RED}Building tests disabled.${NC}" +fi + +if [ -z "$BUILDING_EXAMPLES" ]; then + BUILDING_EXAMPLES="OFF" + echo "${RED}Building examples disabled.${NC}" +fi + +echo "${BLUE}Installation path set to $INSTALL_PREFIX.${NC}" + +if [ -z "$USING_HiCMA" ]; then + echo "${RED}Using HiCMA is disabled.${NC}" +fi + +if [ -z "$USE_CUDA" ]; then + USE_CUDA="OFF" + echo "${RED}Using CUDA disabled${NC}" +fi + +if [ -z "$USE_MPI" ]; then + USE_MPI="OFF" + echo "${RED}Using MPI disabled${NC}" +fi + +if [ "$SHOW_WARNINGS" = "ON" ]; then + COMPILE_FLAGS="$COMPILE_FLAGS -W" + DEVELOPER_WARNINGS="" +elif [ "$SHOW_WARNINGS" = "OFF" ]; then + COMPILE_FLAGS="$COMPILE_FLAGS -w" +fi + +if [ -z "$USE_R" ]; then + USE_R="OFF" + echo "${RED}Using R is disabled${NC}" +fi + +echo "" +echo "${YELLOW}Use -h to print the usages of exageostat-cpp flags.${NC}" +echo "" + +# cleaning bin rm -rf bin/ mkdir bin/ -cmake $DEVELOPER_WARNINGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ +cmake "$DEVELOPER_WARNINGS" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCMAKE_BUILD_TYPE=RELEASE \ -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ -DBUILD_TESTS="${BUILDING_TESTS}" \ @@ -43,7 +176,7 @@ cmake $DEVELOPER_WARNINGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCMAKE_VERBOSE_MAKEFILE:BOOL=${VERBOSE} \ -DUSE_CUDA="${USE_CUDA}" \ -DUSE_MPI="${USE_MPI}" \ - -DUSE_R=ON \ + -DUSE_R="${USE_R}" \ -DBLA_VENDOR="${BLAS_VENDOR}" \ -DCREATE_PACKAGE="${PACKAGE}" \ -DBUILD_SHARED_LIBS=OFF \ @@ -53,19 +186,22 @@ cmake $DEVELOPER_WARNINGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCMAKE_CXX_FLAGS_DEBUG="$COMPILE_FLAGS" \ -DCMAKE_CXX_FLAGS_RELEASE="$COMPILE_FLAGS" -# Change to the bin directory, or exit if it doesn't exist. -cd bin/ || { - echo "Error: bin directory not found." - exit 1 -} +if [ "$USE_R" = "ON" ]; then + # Change to the bin directory, or exit if it doesn't exist. + cd bin/ || { + echo "Error: bin directory not found." + exit 1 + } -# Clean the directory and build the code with the specified options. -cmake --build . -j $(nproc) + # Clean the directory and build the code with the specified options. + cmake --build . -j "$(nproc)" -if [[ "$OSTYPE" == "darwin"* ]]; then - cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.dylib" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.dylib -> src" -else - cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.so" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.so -> src" -fi + if [ "$OS_TYPE" = "darwin"* ]; then + cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.dylib" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.dylib -> src" + else + cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.so" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.so -> src" + fi + + rm -rf "${ABSOLUTE_PATH:?}/bin/"* -rm -rf "${ABSOLUTE_PATH}/bin/" +fi diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 39b38388..ca8e0208 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief Configures and generates documentation using Doxygen. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-12 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index fd6d1ce3..661d1098 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief Includes subdirectories for different modules of the ExaGeoStat software package. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-31 diff --git a/examples/configurations/CMakeLists.txt b/examples/configurations/CMakeLists.txt index 2c556898..12341da4 100644 --- a/examples/configurations/CMakeLists.txt +++ b/examples/configurations/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief Defines an executable and links it with the ExaGeoStat library and other libraries. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-31 diff --git a/examples/configurations/ConfigurationModule.cpp b/examples/configurations/ConfigurationModule.cpp index 975b0897..d7f348d3 100644 --- a/examples/configurations/ConfigurationModule.cpp +++ b/examples/configurations/ConfigurationModule.cpp @@ -8,21 +8,20 @@ * @brief Demonstrates how to use the Configurations class from the ExaGeoStat software package. * @details This file demonstrates how to use the Configurations class from the ExaGeoStat software package * to obtain user-defined configurations for generating synthetic data. -* @version 1.0.0 +* @version 1.1.0 * @author Mahmoud ElKarargy -* @date 2023-01-31 +* @date 2024-02-04 * **/ #include -#include +#include #include using namespace std; using namespace exageostat::common; -using namespace exageostat::configurations; /** * @brief The main function of the program. diff --git a/examples/data-generators/CMakeLists.txt b/examples/data-generators/CMakeLists.txt index d90f9abe..eafd867d 100644 --- a/examples/data-generators/CMakeLists.txt +++ b/examples/data-generators/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief CMake file for building the Example_Synthetic_Data_Generation executable. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-03-04 diff --git a/examples/data-generators/SyntheticDataGeneration.cpp b/examples/data-generators/SyntheticDataGeneration.cpp index e9fa1465..214499f0 100644 --- a/examples/data-generators/SyntheticDataGeneration.cpp +++ b/examples/data-generators/SyntheticDataGeneration.cpp @@ -6,9 +6,9 @@ /** * @file SyntheticLocationsGeneration.cpp * @brief This file contains the main function for generating synthetic Locations for ExaGeoStat - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-03-04 + * @date 2024-02-04 **/ #include @@ -17,10 +17,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::generators; using namespace exageostat::common; -using namespace exageostat::hardware; using namespace exageostat::kernels; /** diff --git a/examples/end-to-end/CMakeLists.txt b/examples/end-to-end/CMakeLists.txt index bfce4c30..9ed3a380 100644 --- a/examples/end-to-end/CMakeLists.txt +++ b/examples/end-to-end/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-31 diff --git a/examples/end-to-end/DataGeneration.cpp b/examples/end-to-end/DataGeneration.cpp index cc454722..f7c22d0a 100644 --- a/examples/end-to-end/DataGeneration.cpp +++ b/examples/end-to-end/DataGeneration.cpp @@ -7,17 +7,15 @@ * @file DataGeneration.cpp * @brief This program either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data. * @details The program takes command line arguments to configure the data generation. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-05-30 + * @date 2024-02-04 **/ -#include +#include #include -using namespace exageostat::configurations; using namespace exageostat::api; -using namespace exageostat::hardware; using namespace exageostat::dataunits; /** @@ -37,7 +35,7 @@ int main(int argc, char **argv) { auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. - std::unique_ptr> data; + std::unique_ptr> data; ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); return 0; diff --git a/examples/end-to-end/DataGenerationAndModeling.cpp b/examples/end-to-end/DataGenerationAndModeling.cpp index caf00a63..dd461e74 100644 --- a/examples/end-to-end/DataGenerationAndModeling.cpp +++ b/examples/end-to-end/DataGenerationAndModeling.cpp @@ -7,17 +7,15 @@ * @file DataGenerationAndModeling.cpp * @brief This program either generates synthetic data using the ExaGeoStat library, or reads an CSV file. * @details The program takes command line arguments to configure the data generation. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-06-21 + * @date 2024-02-04 **/ -#include +#include #include -using namespace exageostat::configurations; using namespace exageostat::api; -using namespace exageostat::hardware; using namespace exageostat::dataunits; /** @@ -37,7 +35,7 @@ int main(int argc, char **argv) { auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. - std::unique_ptr> data; + std::unique_ptr> data; ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); // Modeling module. ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); diff --git a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp index ef39d133..1c80a3b4 100644 --- a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp +++ b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp @@ -7,17 +7,15 @@ * @file DataGenerationModelingAndPrediction.cpp * @brief This program This program either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data, performs data modeling on loaded data, then predicts missing measurements using the ExaGeoStat library. * @details The program takes command line arguments to configure the data generation. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-06-21 + * @date 2024-02-04 **/ -#include +#include #include -using namespace exageostat::configurations; using namespace exageostat::api; -using namespace exageostat::hardware; using namespace exageostat::dataunits; /** @@ -37,7 +35,7 @@ int main(int argc, char **argv) { auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. - std::unique_ptr> data; + std::unique_ptr> data; ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); // Modeling module. ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); diff --git a/examples/end-to-end/DataModeling.cpp b/examples/end-to-end/DataModeling.cpp index 6a4b3fb2..be37b051 100644 --- a/examples/end-to-end/DataModeling.cpp +++ b/examples/end-to-end/DataModeling.cpp @@ -7,20 +7,18 @@ * @file DataModeling.cpp * @brief This program models data using the ExaGeoStat library. * @details The program takes command line arguments and example variables to configure the data modeling. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-06-21 + * @date 2024-02-04 **/ #include -#include +#include #include using namespace exageostat::api; using namespace exageostat::dataunits; -using namespace exageostat::configurations; -using namespace exageostat::hardware; /** * @brief Main entry point for the Data Modeling program. diff --git a/examples/end-to-end/DataPrediction.cpp b/examples/end-to-end/DataPrediction.cpp index 2f681566..e7df1d95 100644 --- a/examples/end-to-end/DataPrediction.cpp +++ b/examples/end-to-end/DataPrediction.cpp @@ -7,17 +7,15 @@ * @file DataPrediction.cpp * @brief This program predicts missing measurements using the ExaGeoStat library. * @details The program takes command line arguments to configure the data prediction module. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-09-11 + * @date 2024-02-04 **/ -#include +#include #include -using namespace exageostat::configurations; using namespace exageostat::api; -using namespace exageostat::hardware; using namespace exageostat::dataunits; /** diff --git a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp new file mode 100644 index 00000000..10740540 --- /dev/null +++ b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp @@ -0,0 +1,75 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file FunctionAdapter.hpp + * @brief Header file for function adapters in the ExaGeoStat software. + * @details It provides declarations for functions that adapt and initialize statistical models or algorithms based on user-defined configurations. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-29 +**/ + +#ifndef EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP +#define EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP + +#include +#include +#include + +#include +#include + +namespace exageostat::adapters { + + /** + * @brief Initializes and configures the R arguments for ExaGeoStat computations. + * @details This function prepares the necessary configurations required by ExaGeoStat to perform statistical computations. + * It includes setting up problem sizes, computational kernels, grid configurations, and other parameters essential + * for the execution of the ExaGeoStat algorithms. + * @param[in] aProblemSize The size of the problem to be solved. + * @param[in] aKernelName The name of the computational kernel to be used. + * @param[in] aTileSize A vector specifying the size of each tile in the computation. + * @param[in] aP_QGrid A vector defining the P x Q process grid. + * @param[in] aTimeSlot The time slot allocated for the computation. + * @param[in] aComputation The type of computation to be performed (e.g., estimation, prediction). + * @param[in] aPrecision The precision (e.g., single, double) of the computation. + * @param[in] aCoresGPUsNumber A vector specifying the number of CPU cores or GPUs to be used. + * @param[in] aBand The bandwidth of the problem, relevant for band-limited computations. + * @param[in] aMaxRank The maximum rank for low-rank approximations. + * @param[in] aInitialTheta A vector of initial values for the model parameters (theta). + * @param[in] aLowerUpperBounds A 2D vector specifying the lower and upper bounds for model parameters. + * @param[in] aEstimatedTheta A vector of estimated values for the model parameters after computation. + * @param[in] aVerbose A string indicating the verbosity level of the output. + * @param[in] aDimension The dimensionality of the problem (e.g., 2D, 3D). + * @param[in] aMaxMleIterations The maximum number of iterations for the Maximum Likelihood Estimation (MLE) algorithm. + * @param[in] aTolerance The tolerance threshold for convergence in iterative algorithms. + * @param[in] aPrediction A vector indicating prediction locations or settings. + * @return A pointer to a Configurations object containing the initialized settings. + * + */ + Configurations * + R_InitializeArguments(const int &aProblemSize, const std::string &aKernelName, const std::vector &aTileSize, + const std::vector &aP_QGrid, const int &aTimeSlot, const std::string &aComputation, + const std::string &aPrecision, const std::vector &aCoresGPUsNumber, const int &aBand, + const int &aMaxRank, const std::vector &aInitialTheta, + const std::vector> &aLowerUpperBounds, + const std::vector &aEstimatedTheta, const std::string &aVerbose, + const std::string &aDimension, const int &aMaxMleIterations, const double &aTolerance, + const std::vector &aPrediction + ); + + /** + * @brief Executes the ExaGeoStat main API functions based on provided hardware and configurations. + * @details This function serves as an entry point to the ExaGeoStat library, allowing users to execute statistical models and algorithms by passing in hardware configurations and computational settings. + * It integrates the hardware layer with the statistical computations, ensuring that the ExaGeoStat algorithms can efficiently utilize the available resources. + * This function is crucial for the seamless operation of the ExaGeoStat software, facilitating the execution of complex statistical tasks on diverse hardware configurations. + * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. + * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. + */ + void R_ExaGeoStatAPI(ExaGeoStatHardware *apHardware, Configurations *apConfigurations); + +} +#endif //EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP diff --git a/inst/include/api/ExaGeoStat.hpp b/inst/include/api/ExaGeoStat.hpp index 9a1aa9b7..c967dd39 100644 --- a/inst/include/api/ExaGeoStat.hpp +++ b/inst/include/api/ExaGeoStat.hpp @@ -6,9 +6,9 @@ /** * @file ExaGeoStat.hpp * @brief High-Level Wrapper class containing the static API for ExaGeoStat operations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-05-30 + * @date 2024-02-04 **/ #ifndef EXAGEOSTATCPP_EXAGEOSTAT_HPP @@ -39,8 +39,8 @@ namespace exageostat::api { * @return void * */ - static void ExaGeoStatLoadData(const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfigurations, + static void ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, + Configurations &aConfigurations, std::unique_ptr> &aData); /** @@ -52,8 +52,8 @@ namespace exageostat::api { * @return void * */ - static T ExaGeoStatDataModeling(const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfigurations, + static T ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, + Configurations &aConfigurations, std::unique_ptr> &aData, T *apMeasurementsMatrix = nullptr); @@ -75,8 +75,8 @@ namespace exageostat::api { * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. * @return void */ - static void ExaGeoStatPrediction(const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfigurations, + static void ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, + Configurations &aConfigurations, std::unique_ptr> &aData, T *apMeasurementsMatrix = nullptr); diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index 935c78a7..146f7fb6 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -5,7 +5,7 @@ /** * @file Definitions.hpp - * @version 1.0.1 + * @version 1.1.0 * @brief This file contains common definitions used in ExaGeoStat software package. * @details These definitions include enums for dimension, computation, precision, and floating point arithmetic; * A macro for instantiating template classes with supported types; and a set of available kernels. diff --git a/inst/include/common/PluginRegistry.hpp b/inst/include/common/PluginRegistry.hpp index 23df532f..dcf14845 100644 --- a/inst/include/common/PluginRegistry.hpp +++ b/inst/include/common/PluginRegistry.hpp @@ -6,7 +6,7 @@ /** * @file PluginRegistry.hpp * @brief Defines a template class for registering and creating plugins. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-04-30 **/ diff --git a/inst/include/common/Utils.hpp b/inst/include/common/Utils.hpp deleted file mode 100644 index f0428098..00000000 --- a/inst/include/common/Utils.hpp +++ /dev/null @@ -1,106 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file Utils.hpp - * @brief This file contains common functions used in ExaGeoStat software package. - * @details These functions include so far the VERBOSE macro that prints a message - * to the console if the verbosity setting is set to "verbose mode. - * @version 1.0.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @date 2023-03-21 -**/ - -#ifndef EXAGEOSTATCPP_UTILS_HPP -#define EXAGEOSTATCPP_UTILS_HPP - -#include -#include -#include - -#include -#include -#include - -/** - * DEFAULT_PRECISION the value of the default C++ std::cout number of precision. - */ -#define DEFAULT_PRECISION 6 - -/** -* Verbose macro for logging and debugging mode -*/ -#define VERBOSE(msg) \ - if(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::DETAILED_MODE && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) \ - std::cout << "\t\t\t " << msg << std::endl; - -/** - * LOGGER_1 macro for logging outputs with double taps and new line at the end. - */ - -#define LOGGER_1(msg) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) \ - std::cout << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg << std::endl; - -/** - * LOGGER_2 macro for logging outputs with double taps and without new line at the end. - */ -#define LOGGER_2(msg, A) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) \ - std::cout << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; - -/** - * LOGGER_CONTROL is The internal macro that simply strips the excess and ends up with the required macro - */ -#define LOGGER_CONTROL(x, A, B, FUNC, ...) FUNC - -/** - * LOGGER macro that's called, Used to logging outputs - */ -#define LOGGER(...) LOGGER_CONTROL(,##__VA_ARGS__, \ - LOGGER_2(__VA_ARGS__), \ - LOGGER_1(__VA_ARGS__), \ - ) - -/** -* LOGGER_PRECISION_1 macro for logging outputs without any taps, without new line at the end and with customized precision. -*/ -#define LOGGER_PRECISION_1(msg, precision) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) \ - std::cout << std::fixed << std::setprecision(precision) << msg; - -/** -* LOGGER_PRECISION macro for logging outputs without any taps, without new line at the end and with default C++ precision. -*/ -#define LOGGER_PRECISION_2(msg) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) \ - std::cout << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; - -/** - * LOGGER_CONTROL is The internal macro that simply strips the excess and ends up with the required macro - */ -#define LOGGER_PRECISION_CONTROL(x, A, B, FUNC, ...) FUNC - -/** - * LOGGER macro that's called, Used to logging outputs - */ -#define LOGGER_PRECISION(...) LOGGER_PRECISION_CONTROL(,##__VA_ARGS__, \ - LOGGER_PRECISION_1(__VA_ARGS__), \ - LOGGER_PRECISION_2(__VA_ARGS__), \ - ) - -/** - * Timing macro to start timing. - */ -#define START_TIMING(t) auto t##_start = std::chrono::high_resolution_clock::now() - -/** - * Timing macro to stop timing. - */ -#define STOP_TIMING(t) auto t##_end = std::chrono::high_resolution_clock::now(); \ - t = std::chrono::duration_cast>(t##_end - t##_start).count() - -#endif //EXAGEOSTATCPP_UTILS_HPP diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index 47968bc2..4c3a9967 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -5,11 +5,11 @@ /** * @file Configurations.hpp -* @version 1.0.1 +* @version 1.1.0 * @brief Contains the declaration of the Configurations class and its member functions. -* @author Sameh Abdulah * @author Mahmoud ElKarargy -* @date 2023-01-31 +* @author Sameh Abdulah +* @date 2024-02-04 **/ #ifndef EXAGEOSTAT_CPP_CONFIGURATIONS_HPP @@ -59,394 +59,384 @@ type Get##name() return std::any_cast(mDictionary[dictionary_name]); \ } -namespace exageostat::configurations { +/** + * @class Configurations + * @brief Contains methods to set and get. + * + */ +class Configurations { +public: + /** - * @class Configurations - * @brief Contains methods to set and get. + * @brief Constructor initializing a Configuration object with default values. * */ - class Configurations { - public: + Configurations(); - /** - * @brief Constructor initializing a Configuration object with default values. - * - */ - Configurations(); + /** + * @brief Virtual destructor to allow calls to the correct concrete destructor. + * + */ + ~Configurations(); - /** - * @brief Virtual destructor to allow calls to the correct concrete destructor. - * - */ - virtual ~Configurations() = default; + /** + * @brief Initialize the module arguments. + * @param[in] aArgC The number of arguments being passed into the program from the command line. + * @param[in] apArgV The array of arguments. + * @details This method initializes the command line arguments and set default values for unused args. + * + */ + void InitializeArguments(const int &aArgC, char **apArgV); + + /** + * @brief Initialize the module arguments. + * @param[in] aArgC The number of arguments being passed into the program from the command line. + * @param[in] apArgV The array of arguments. + * @details This method initializes the command line arguments and set default values for unused args. + * + */ - /** - * @brief Initialize the module arguments. - * @param[in] aArgC The number of arguments being passed into the program from the command line. - * @param[in] apArgV The array of arguments. - * @details This method initializes the command line arguments and set default values for unused args. - * - */ - void InitializeArguments(const int &aArgC, char **apArgV); + /** + * @brief Initialize the all theta arguments. + * @return void + */ + void InitializeAllTheta(); - /** - * @brief Initialize the all theta arguments. - * @return void - */ - void InitializeAllTheta(); + /** + * @brief Initialize data generation arguments.. + * @return void + * + */ + void InitializeDataGenerationArguments(); - /** - * @brief Initialize data generation arguments.. - * @return void - * - */ - void InitializeDataGenerationArguments(); + /** + * @brief Initialize data Modeling arguments. + * @return void + * + */ + void InitializeDataModelingArguments(); - /** - * @brief Initialize data Modeling arguments. - * @return void - * - */ - void InitializeDataModelingArguments(); + /** + * @brief Initialize data Prediction arguments. + * @return void + * + */ + void InitializeDataPredictionArguments(); - /** - * @brief Initialize data Prediction arguments. - * @return void - * - */ - void InitializeDataPredictionArguments(); + /** + * @brief Print the usage and accepted Arguments. + * @return void + * + */ + static void PrintUsage(); - /** - * @brief Print the usage and accepted Arguments. - * @return void - * - */ - static void PrintUsage(); + /** START OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ - /** START OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ + CREATE_SETTER_FUNCTION(ProblemSize, int, aProblemSize, "ProblemSize") - CREATE_SETTER_FUNCTION(ProblemSize, int, aProblemSize, "ProblemSize") + CREATE_GETTER_FUNCTION(ProblemSize, int, "ProblemSize") - CREATE_GETTER_FUNCTION(ProblemSize, int, "ProblemSize") + CREATE_SETTER_FUNCTION(KernelName, const std::string&, aKernel, "Kernel") - CREATE_SETTER_FUNCTION(KernelName, const std::string&, aKernel, "Kernel") + CREATE_GETTER_FUNCTION(KernelName, const std::string&, "Kernel") - CREATE_GETTER_FUNCTION(KernelName, const std::string&, "Kernel") + CREATE_SETTER_FUNCTION(PGrid, int, aPGrid, "PGrid") - CREATE_SETTER_FUNCTION(PGrid, int, aPGrid, "PGrid") + CREATE_GETTER_FUNCTION(PGrid, int, "PGrid") - CREATE_GETTER_FUNCTION(PGrid, int, "PGrid") + CREATE_SETTER_FUNCTION(QGrid, int, aQGrid, "QGrid") - CREATE_SETTER_FUNCTION(QGrid, int, aQGrid, "QGrid") + CREATE_GETTER_FUNCTION(QGrid, int, "QGrid") - CREATE_GETTER_FUNCTION(QGrid, int, "QGrid") + CREATE_SETTER_FUNCTION(TimeSlot, int, aTimeSlot, "TimeSlot") - CREATE_SETTER_FUNCTION(TimeSlot, int, aTimeSlot, "TimeSlot") + CREATE_GETTER_FUNCTION(TimeSlot, int, "TimeSlot") - CREATE_GETTER_FUNCTION(TimeSlot, int, "TimeSlot") + CREATE_SETTER_FUNCTION(Computation, exageostat::common::Computation, aComputation, "Computation") - CREATE_SETTER_FUNCTION(Computation, common::Computation, aComputation, "Computation") + CREATE_GETTER_FUNCTION(Computation, exageostat::common::Computation, "Computation") - CREATE_GETTER_FUNCTION(Computation, common::Computation, "Computation") + CREATE_SETTER_FUNCTION(Precision, exageostat::common::Precision, aPrecision, "Precision") - CREATE_SETTER_FUNCTION(Precision, common::Precision, aPrecision, "Precision") + CREATE_GETTER_FUNCTION(Precision, exageostat::common::Precision, "Precision") - CREATE_GETTER_FUNCTION(Precision, common::Precision, "Precision") + CREATE_SETTER_FUNCTION(CoresNumber, int, aCoresNumbers, "CoresNumbers") - CREATE_SETTER_FUNCTION(CoresNumber, int, aCoresNumbers, "CoresNumbers") + CREATE_GETTER_FUNCTION(CoresNumber, int, "CoresNumbers") - CREATE_GETTER_FUNCTION(CoresNumber, int, "CoresNumbers") + CREATE_SETTER_FUNCTION(GPUsNumbers, int, aGPUsNumber, "GPUsNumbers") - CREATE_SETTER_FUNCTION(GPUsNumbers, int, aGPUsNumber, "GPUsNumbers") + CREATE_GETTER_FUNCTION(GPUsNumbers, int, "GPUsNumbers") - CREATE_GETTER_FUNCTION(GPUsNumbers, int, "GPUsNumbers") + CREATE_SETTER_FUNCTION(DenseTileSize, int, aTileSize, "DTS") - CREATE_SETTER_FUNCTION(DenseTileSize, int, aTileSize, "DTS") + CREATE_GETTER_FUNCTION(DenseTileSize, int, "DTS") - CREATE_GETTER_FUNCTION(DenseTileSize, int, "DTS") + CREATE_SETTER_FUNCTION(LowTileSize, int, aTileSize, "LTS") - CREATE_SETTER_FUNCTION(LowTileSize, int, aTileSize, "LTS") + CREATE_GETTER_FUNCTION(LowTileSize, int, "LTS") - CREATE_GETTER_FUNCTION(LowTileSize, int, "LTS") + CREATE_SETTER_FUNCTION(Band, int, aBand, "Band") - CREATE_SETTER_FUNCTION(Band, int, aBand, "Band") + CREATE_GETTER_FUNCTION(Band, int, "Band") - CREATE_GETTER_FUNCTION(Band, int, "Band") + CREATE_SETTER_FUNCTION(MaxRank, int, aMaxRank, "MaxRank") - CREATE_SETTER_FUNCTION(MaxRank, int, aMaxRank, "MaxRank") + CREATE_GETTER_FUNCTION(MaxRank, int, "MaxRank") - CREATE_GETTER_FUNCTION(MaxRank, int, "MaxRank") + CREATE_SETTER_FUNCTION(ActualObservationsFilePath, const std::string &, aActualObservationsFilePath, + "ActualObservationsFilePath") - CREATE_SETTER_FUNCTION(ActualObservationsFilePath, const std::string &, aActualObservationsFilePath, - "ActualObservationsFilePath") + CREATE_GETTER_FUNCTION(ActualObservationsFilePath, std::string, "ActualObservationsFilePath") - CREATE_GETTER_FUNCTION(ActualObservationsFilePath, std::string, "ActualObservationsFilePath") + CREATE_SETTER_FUNCTION(Seed, int, aSeed, "Seed") - CREATE_SETTER_FUNCTION(Seed, int, aSeed, "Seed") + CREATE_GETTER_FUNCTION(Seed, int, "Seed") - CREATE_GETTER_FUNCTION(Seed, int, "Seed") + CREATE_SETTER_FUNCTION(LoggerPath, const std::string&, aLoggerPath, "LoggerPath") - CREATE_SETTER_FUNCTION(LoggerPath, const std::string&, aLoggerPath, "LoggerPath") + CREATE_GETTER_FUNCTION(LoggerPath, std::string, "LoggerPath") - CREATE_GETTER_FUNCTION(LoggerPath, std::string, "LoggerPath") + CREATE_SETTER_FUNCTION(InitialTheta, std::vector &, apTheta, "InitialTheta") - CREATE_SETTER_FUNCTION(InitialTheta, std::vector &, apTheta, "InitialTheta") + CREATE_GETTER_FUNCTION(InitialTheta, std::vector &, "InitialTheta") - CREATE_GETTER_FUNCTION(InitialTheta, std::vector &, "InitialTheta") + CREATE_SETTER_FUNCTION(IsOOC, bool, aIsOOC, "OOC") - CREATE_SETTER_FUNCTION(IsOOC, bool, aIsOOC, "OOC") + CREATE_GETTER_FUNCTION(IsOOC, bool, "OOC") - CREATE_GETTER_FUNCTION(IsOOC, bool, "OOC") + CREATE_SETTER_FUNCTION(ApproximationMode, int, aApproximationMode, "ApproximationMode") - CREATE_SETTER_FUNCTION(ApproximationMode, int, aApproximationMode, "ApproximationMode") + CREATE_GETTER_FUNCTION(ApproximationMode, int, "ApproximationMode") - CREATE_GETTER_FUNCTION(ApproximationMode, int, "ApproximationMode") + CREATE_SETTER_FUNCTION(Logger, bool, aLogger, "Logger") - CREATE_SETTER_FUNCTION(Logger, bool, aLogger, "Logger") + CREATE_GETTER_FUNCTION(Logger, bool, "Logger") - CREATE_GETTER_FUNCTION(Logger, bool, "Logger") + CREATE_SETTER_FUNCTION(LowerBounds, std::vector &, apTheta, "LowerBounds") - CREATE_SETTER_FUNCTION(MeanSquareError, double, aMeanSquareError, "MeanSquareError") + CREATE_GETTER_FUNCTION(LowerBounds, std::vector &, "LowerBounds") - CREATE_GETTER_FUNCTION(MeanSquareError, double, "MeanSquareError") + CREATE_SETTER_FUNCTION(UpperBounds, std::vector &, apTheta, "UpperBounds") - CREATE_SETTER_FUNCTION(LowerBounds, std::vector &, apTheta, "LowerBounds") + CREATE_GETTER_FUNCTION(UpperBounds, std::vector &, "UpperBounds") - CREATE_GETTER_FUNCTION(LowerBounds, std::vector &, "LowerBounds") + CREATE_SETTER_FUNCTION(EstimatedTheta, std::vector &, apTheta, "EstimatedTheta") - CREATE_SETTER_FUNCTION(UpperBounds, std::vector &, apTheta, "UpperBounds") + CREATE_GETTER_FUNCTION(EstimatedTheta, std::vector &, "EstimatedTheta") - CREATE_GETTER_FUNCTION(UpperBounds, std::vector &, "UpperBounds") + CREATE_SETTER_FUNCTION(StartingTheta, std::vector &, apTheta, "StartingTheta") - CREATE_SETTER_FUNCTION(EstimatedTheta, std::vector &, apTheta, "EstimatedTheta") + CREATE_GETTER_FUNCTION(StartingTheta, std::vector &, "StartingTheta") - CREATE_GETTER_FUNCTION(EstimatedTheta, std::vector &, "EstimatedTheta") + CREATE_SETTER_FUNCTION(IsNonGaussian, bool, aIsNonGaussian, "IsNonGaussian") - CREATE_SETTER_FUNCTION(StartingTheta, std::vector &, apTheta, "StartingTheta") + CREATE_GETTER_FUNCTION(IsNonGaussian, bool, "IsNonGaussian") - CREATE_GETTER_FUNCTION(StartingTheta, std::vector &, "StartingTheta") + /** + * @brief Getter for the verbosity. + * @return The verbosity mode. + * + */ + static exageostat::common::Verbose GetVerbosity(); - CREATE_SETTER_FUNCTION(IsNonGaussian, bool, aIsNonGaussian, "IsNonGaussian") + static void SetVerbosity(const exageostat::common::Verbose &aVerbose); - CREATE_GETTER_FUNCTION(IsNonGaussian, bool, "IsNonGaussian") + /** END OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ + /** START OF THE DATA GENERATION MODULES. **/ - /** - * @brief Getter for the verbosity. - * @return The verbosity mode. - * - */ - static exageostat::common::Verbose GetVerbosity(); + CREATE_SETTER_FUNCTION(Dimension, exageostat::common::Dimension, aDimension, "Dimension") - static void SetVerbosity(const common::Verbose &aVerbose); + CREATE_GETTER_FUNCTION(Dimension, exageostat::common::Dimension, "Dimension") - /** END OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ - /** START OF THE DATA GENERATION MODULES. **/ + CREATE_SETTER_FUNCTION(IsSynthetic, bool, aIsSynthetic, "IsSynthetic") - CREATE_SETTER_FUNCTION(Dimension, exageostat::common::Dimension, aDimension, "Dimension") + CREATE_GETTER_FUNCTION(IsSynthetic, bool, "IsSynthetic") - CREATE_GETTER_FUNCTION(Dimension, exageostat::common::Dimension, "Dimension") + CREATE_SETTER_FUNCTION(DataPath, const std::string&, aDataPath, "DataPath") - CREATE_SETTER_FUNCTION(IsSynthetic, bool, aIsSynthetic, "IsSynthetic") + CREATE_GETTER_FUNCTION(DataPath, std::string, "DataPath") - CREATE_GETTER_FUNCTION(IsSynthetic, bool, "IsSynthetic") + /** END OF THE DATA GENERATION MODULES. **/ + /** START OF THE DATA MODELING MODULES. **/ - CREATE_SETTER_FUNCTION(IsCSV, bool, aIsCSV, "IsCSV") + CREATE_SETTER_FUNCTION(RecoveryFile, const std::string&, aRecoveryFile, "RecoveryFile") - CREATE_GETTER_FUNCTION(IsCSV, bool, "IsCSV") + CREATE_GETTER_FUNCTION(RecoveryFile, std::string, "RecoveryFile") - CREATE_SETTER_FUNCTION(DataPath, const std::string&, aDataPath, "DataPath") + CREATE_SETTER_FUNCTION(FileLogPath, FILE *, apFileLogPath, "FileLogPath") - CREATE_GETTER_FUNCTION(DataPath, std::string, "DataPath") + CREATE_GETTER_FUNCTION(FileLogPath, FILE *, "FileLogPath") - /** END OF THE DATA GENERATION MODULES. **/ - /** START OF THE DATA MODELING MODULES. **/ + CREATE_SETTER_FUNCTION(FileLogName, const std::string&, aFileLogName, "FileLogName") - CREATE_SETTER_FUNCTION(RecoveryFile, const std::string&, aRecoveryFile, "RecoveryFile") + CREATE_GETTER_FUNCTION(FileLogName, std::string, "FileLogName") - CREATE_GETTER_FUNCTION(RecoveryFile, std::string, "RecoveryFile") + CREATE_SETTER_FUNCTION(DistanceMetric, exageostat::common::DistanceMetric, aDistanceMetric, "DistanceMetric") - CREATE_SETTER_FUNCTION(FileLogPath, FILE *, apFileLogPath, "FileLogPath") + CREATE_GETTER_FUNCTION(DistanceMetric, exageostat::common::DistanceMetric, "DistanceMetric") - CREATE_GETTER_FUNCTION(FileLogPath, FILE *, "FileLogPath") + CREATE_SETTER_FUNCTION(MaxMleIterations, int, aMaxMleIterations, "MaxMleIterations") - CREATE_SETTER_FUNCTION(FileLogName, const std::string&, aFileLogName, "FileLogName") + CREATE_GETTER_FUNCTION(MaxMleIterations, int, "MaxMleIterations") - CREATE_GETTER_FUNCTION(FileLogName, std::string, "FileLogName") + CREATE_SETTER_FUNCTION(Accuracy, int, aAccuracy, "Accuracy") - CREATE_SETTER_FUNCTION(AvgExecutedTimePerIteration, double, aAvgExecTimePerIter, "AvgExecuted") + CREATE_GETTER_FUNCTION(Accuracy, int, "Accuracy") - CREATE_GETTER_FUNCTION(AvgExecutedTimePerIteration, double, "AvgExecuted") + CREATE_SETTER_FUNCTION(Tolerance, double, aTolerance, "Tolerance") - CREATE_SETTER_FUNCTION(AvgFlopsPerIteration, double, aAvgFlopsPerIter, "AvgFlops") + CREATE_GETTER_FUNCTION(Tolerance, double, "Tolerance") - CREATE_GETTER_FUNCTION(AvgFlopsPerIteration, double, "AvgFlops") + /** END OF THE DATA MODELING MODULES. **/ + /** START OF THE DATA PREDICTION MODULES. **/ - CREATE_SETTER_FUNCTION(DistanceMetric, common::DistanceMetric, aDistanceMetric, "DistanceMetric") + CREATE_SETTER_FUNCTION(UnknownObservationsNb, int, aUnknownObservationsNumber, "UnknownObservationsNb") - CREATE_GETTER_FUNCTION(DistanceMetric, common::DistanceMetric, "DistanceMetric") + CREATE_GETTER_FUNCTION(UnknownObservationsNb, int, "UnknownObservationsNb") - CREATE_SETTER_FUNCTION(MaxMleIterations, int, aMaxMleIterations, "MaxMleIterations") + CREATE_SETTER_FUNCTION(IsMSPE, bool, aIsMSPE, "IsMSPE") - CREATE_GETTER_FUNCTION(MaxMleIterations, int, "MaxMleIterations") + CREATE_GETTER_FUNCTION(IsMSPE, bool, "IsMSPE") - CREATE_SETTER_FUNCTION(Accuracy, int, aAccuracy, "Accuracy") + CREATE_SETTER_FUNCTION(IsIDW, bool, aIsIDW, "IsIDW") - CREATE_GETTER_FUNCTION(Accuracy, int, "Accuracy") + CREATE_GETTER_FUNCTION(IsIDW, bool, "IsIDW") - CREATE_SETTER_FUNCTION(Tolerance, double, aTolerance, "Tolerance") + CREATE_SETTER_FUNCTION(IsMLOEMMOM, bool, aIsMLOEMMOM, "IsMLOEMMOM") - CREATE_GETTER_FUNCTION(Tolerance, double, "Tolerance") + CREATE_GETTER_FUNCTION(IsMLOEMMOM, bool, "IsMLOEMMOM") - /** END OF THE DATA MODELING MODULES. **/ - /** START OF THE DATA PREDICTION MODULES. **/ + CREATE_SETTER_FUNCTION(IsFisher, bool, aIsFisher, "IsFisher") - CREATE_SETTER_FUNCTION(UnknownObservationsNb, int, aUnknownObservationsNumber, "UnknownObservationsNb") + CREATE_GETTER_FUNCTION(IsFisher, bool, "IsFisher") - CREATE_GETTER_FUNCTION(UnknownObservationsNb, int, "UnknownObservationsNb") + /** END OF THE DATA PREDICTION MODULES. **/ - CREATE_SETTER_FUNCTION(IsMSPE, bool, aIsMSPE, "IsMSPE") + /** + * @brief Check if input value is numerical. + * @param[in] aValue The input from the user side. + * @return The int casted value. + * + */ + static int CheckNumericalValue(const std::string &aValue); - CREATE_GETTER_FUNCTION(IsMSPE, bool, "IsMSPE") + /** + * @brief Checks the value of the dimension parameter. + * @param[in] aDimension A string representing the dimension. + * @return The corresponding dimension value. + * + */ + static exageostat::common::Dimension CheckDimensionValue(const std::string &aDimension); - CREATE_SETTER_FUNCTION(IsIDW, bool, aIsIDW, "IsIDW") + /** + * @brief Checks if the kernel value is valid. + * @param[in] aKernel The kernel to check. + * @return void + * + */ + void CheckKernelValue(const std::string &aKernel); - CREATE_GETTER_FUNCTION(IsIDW, bool, "IsIDW") + /** + * @brief Check input computation value. + * @param[in] aValue The input from the user side. + * @return Enum with the selected computation, Error if not exist. + * + */ + static exageostat::common::Computation CheckComputationValue(const std::string &aValue); - CREATE_SETTER_FUNCTION(IsMLOEMMOM, bool, aIsMLOEMMOM, "IsMLOEMMOM") + /** + * @brief Check input precision value. + * @param[in] aValue The input from the user side. + * @return Enum with the selected Precision, Error if not exist. + * + */ + static exageostat::common::Precision CheckPrecisionValue(const std::string &aValue); - CREATE_GETTER_FUNCTION(IsMLOEMMOM, bool, "IsMLOEMMOM") + /** + * @brief Checks the value of the unknown observations parameter. + * @param[in] aValue A string representing the number of unknown observations. + * @return The corresponding integer value. + */ + int CheckUnknownObservationsValue(const std::string &aValue); - CREATE_SETTER_FUNCTION(IsFisher, bool, aIsFisher, "IsFisher") + /** + * @brief Initialize a vector with a given size to contain zeros. + * @param[in, out] aTheta A reference to the vector to initialize. + * @param[in] aSize The size of the vector to initialize. + * @return void. + * + */ + static void InitTheta(std::vector &aTheta, const int &aSize); - CREATE_GETTER_FUNCTION(IsFisher, bool, "IsFisher") + /** + * @brief print the summary of MLE inputs. + * @return void + */ + inline void PrintSummary(); - /** END OF THE DATA PREDICTION MODULES. **/ + /** + * @brief Calculates the number of observed measurements. + * @return number of observed measurements. + */ + int CalculateZObsNumber(); - /** - * @brief Check if input value is numerical. - * @param[in] aValue The input from the user side. - * @return The int casted value. - * - */ - static int CheckNumericalValue(const std::string &aValue); - /** - * @brief Checks the value of the dimension parameter. - * @param[in] aDimension A string representing the dimension. - * @return The corresponding dimension value. - * - */ - static exageostat::common::Dimension CheckDimensionValue(const std::string &aDimension); +private: - /** - * @brief Checks if the kernel value is valid. - * @param[in] aKernel The kernel to check. - * @return void - * - */ - void CheckKernelValue(const std::string &aKernel); + /** + * @brief Checks the run mode and sets the verbosity level. + * @param[in] aVerbosity A string representing the desired run mode ("verbose" or "standard"). + * @throws std::range_error if the input string is not "verbose" or "standard". + * @return void + * + */ + static void ParseVerbose(const std::string &aVerbosity); - /** - * @brief Check input computation value. - * @param[in] aValue The input from the user side. - * @return Enum with the selected computation, Error if not exist. - * - */ - static common::Computation CheckComputationValue(const std::string &aValue); + /** + * @brief Checks if a given string is in camel case format. + * @param[in] aString The string to check. + * @return true if the string is in camel case format, false otherwise. + * + */ + static bool IsCamelCase(const std::string &aString); - /** - * @brief Check input precision value. - * @param[in] aValue The input from the user side. - * @return Enum with the selected Precision, Error if not exist. - * - */ - static common::Precision CheckPrecisionValue(const std::string &aValue); + /** + * @brief Parses a string of theta values and returns an array of doubles. + * @param[in] aInputValues The input string of theta values. + * @return A vector of parsed theta values. + * + */ + static std::vector ParseTheta(const std::string &aInputValues); - /** - * @brief Checks the value of the unknown observations parameter. - * @param[in] aValue A string representing the number of unknown observations. - * @return The corresponding integer value. - */ - int CheckUnknownObservationsValue(const std::string &aValue); + /** + * @brief parse user's input to distance metric. + * @param[in] aDistanceMetric string specifying the used distance metric. + * @return void + */ + void ParseDistanceMetric(const std::string &aDistanceMetric); - /** - * @brief Initialize a vector with a given size to contain zeros. - * @param[in, out] aTheta A reference to the vector to initialize. - * @param[in] aSize The size of the vector to initialize. - * @return void. - * - */ - static void InitTheta(std::vector &aTheta, const int &aSize); - - /** - * @brief print the summary of MLE inputs. - * @return void - */ - inline void PrintSummary(); - - /** - * @brief Calculates the number of observed measurements. - * @return number of observed measurements. - */ - int CalculateZObsNumber(); - - - private: - - /** - * @brief Checks the run mode and sets the verbosity level. - * @param[in] aVerbosity A string representing the desired run mode ("verbose" or "standard"). - * @throws std::range_error if the input string is not "verbose" or "standard". - * @return void - * - */ - static void ParseVerbose(const std::string &aVerbosity); - - /** - * @brief Checks if a given string is in camel case format. - * @param[in] aString The string to check. - * @return true if the string is in camel case format, false otherwise. - * - */ - static bool IsCamelCase(const std::string &aString); - - /** - * @brief Parses a string of theta values and returns an array of doubles. - * @param[in] aInputValues The input string of theta values. - * @return A vector of parsed theta values. - * - */ - static std::vector ParseTheta(const std::string &aInputValues); - - /** - * @brief parse user's input to distance metric. - * @param[in] aDistanceMetric string specifying the used distance metric. - * @return void - */ - void ParseDistanceMetric(const std::string &aDistanceMetric); - - /** - * @brief Initializes the log file. - * @details This function attempts to open a log file with the name returned by GetFileLogName(), - * and sets the file log path accordingly. If an exception occurs during the file opening, - * a default log file named "log_file" is created. - * @return void - */ - void InitLog(); - - /// Used Dictionary - std::unordered_map mDictionary; - /// Used Argument counter - int mArgC = 0; - /// Used Argument vectors - char **mpArgV = nullptr; - //// Used run mode - static exageostat::common::Verbose mVerbosity; - //// Used bool for init theta - static bool mIsThetaInit; - - }; -}//namespace exageostat + /** + * @brief Initializes the log file. + * @details This function attempts to open a log file with the name returned by GetFileLogName(), + * and sets the file log path accordingly. If an exception occurs during the file opening, + * a default log file named "log_file" is created. + * @return void + */ + void InitLog(); + + /// Used Dictionary + std::unordered_map mDictionary; + /// Used Argument counter + int mArgC = 0; + /// Used Argument vectors + char **mpArgV = nullptr; + //// Used run mode + static exageostat::common::Verbose mVerbosity; + //// Used bool for init theta + static bool mIsThetaInit; + +}; #endif //EXAGEOSTAT_CPP_CONFIGURATIONS_HPP diff --git a/inst/include/data-generators/DataGenerator.hpp b/inst/include/data-generators/DataGenerator.hpp index 130065e3..6c5dfd3d 100644 --- a/inst/include/data-generators/DataGenerator.hpp +++ b/inst/include/data-generators/DataGenerator.hpp @@ -6,7 +6,7 @@ /** * @file DataGenerator.hpp * @brief Contains definition for abstract Data Generator Class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-02-14 **/ @@ -42,8 +42,8 @@ namespace exageostat::generators { * */ virtual std::unique_ptr> - CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, + CreateData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) = 0; /** @@ -54,7 +54,7 @@ namespace exageostat::generators { * */ static std::unique_ptr - CreateGenerator(exageostat::configurations::Configurations &aConfigurations); + CreateGenerator(Configurations &aConfigurations); /** * @brief Destructor for the data generator object. @@ -66,8 +66,6 @@ namespace exageostat::generators { protected: /// Used bool identifying type of generation. static bool mIsSynthetic; - /// Used bool identifying type of generation. - static bool mIsCSV; }; /** diff --git a/inst/include/data-generators/concrete/CSVDataGenerator.hpp b/inst/include/data-generators/concrete/CSVDataGenerator.hpp index cb5220b3..0547bb93 100644 --- a/inst/include/data-generators/concrete/CSVDataGenerator.hpp +++ b/inst/include/data-generators/concrete/CSVDataGenerator.hpp @@ -6,7 +6,7 @@ /** * @file CSVDataGenerator.hpp * @brief A class for generating synthetic data. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-14 @@ -40,8 +40,8 @@ namespace exageostat::generators::csv { * @copydoc DataGenerator::CreateData() * */ - std::unique_ptr> CreateData(exageostat::configurations::Configurations &, - const exageostat::hardware::ExaGeoStatHardware &aHardware, + std::unique_ptr> CreateData(Configurations &, + const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) override; /** @@ -54,7 +54,7 @@ namespace exageostat::generators::csv { * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ - void ReadData(exageostat::configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, + void ReadData(Configurations &aConfigurations, std::vector &aMeasurementsMatrix, std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, const int &aP); diff --git a/inst/include/data-generators/concrete/SyntheticGenerator.hpp b/inst/include/data-generators/concrete/SyntheticGenerator.hpp index e73e38be..c3405235 100644 --- a/inst/include/data-generators/concrete/SyntheticGenerator.hpp +++ b/inst/include/data-generators/concrete/SyntheticGenerator.hpp @@ -6,7 +6,7 @@ /** * @file SyntheticGenerator.hpp * @brief A class for generating synthetic data. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-14 @@ -57,8 +57,8 @@ namespace exageostat::generators::synthetic { * */ std::unique_ptr> - CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, + CreateData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) override; /** diff --git a/inst/include/data-units/DescriptorData.hpp b/inst/include/data-units/DescriptorData.hpp index d80e03b6..d5d9f88b 100644 --- a/inst/include/data-units/DescriptorData.hpp +++ b/inst/include/data-units/DescriptorData.hpp @@ -6,7 +6,7 @@ /** * @file DescriptorData.hpp * @brief Contains the definition of the DescriptorData class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-18 diff --git a/inst/include/data-units/ExaGeoStatData.hpp b/inst/include/data-units/ExaGeoStatData.hpp index c35f1ac8..d3c5ce0c 100644 --- a/inst/include/data-units/ExaGeoStatData.hpp +++ b/inst/include/data-units/ExaGeoStatData.hpp @@ -6,10 +6,10 @@ /** * @file ExaGeoStatData.hpp * @brief Contains the definition of the ExaGeoStatData class. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-07-19 + * @date 2024-02-04 **/ #ifndef EXAGEOSTATCPP_EXAGEOSTATDATA_HPP @@ -38,6 +38,13 @@ namespace exageostat::dataunits { */ ExaGeoStatData(const int &aSize, const exageostat::common::Dimension &aDimension); + /** + * @brief Constructor for ExaGeoStatData. + * @param[in] aSize The size of the data. + * @param[in] aDimension The dimension of the data. + */ + ExaGeoStatData(const int &aSize, const std::string &aDimension); + /** * @brief Default constructor for ExaGeoStatData. */ diff --git a/inst/include/data-units/Locations.hpp b/inst/include/data-units/Locations.hpp index 032be1f9..77f0c0ae 100644 --- a/inst/include/data-units/Locations.hpp +++ b/inst/include/data-units/Locations.hpp @@ -6,7 +6,7 @@ /** * @file Locations.hpp * @brief Header file for the Locations class, which contains methods to set and get location data. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-27 diff --git a/inst/include/data-units/ModelingDataHolders.hpp b/inst/include/data-units/ModelingDataHolders.hpp index a6f932ea..0d858dc6 100644 --- a/inst/include/data-units/ModelingDataHolders.hpp +++ b/inst/include/data-units/ModelingDataHolders.hpp @@ -1,7 +1,7 @@ /** * @file ModelingDataHolders.hpp * @brief This file contains the definition of the mModelingData struct, which contains all the data needed for modeling. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-08-24 **/ @@ -18,11 +18,11 @@ namespace exageostat::dataunits { template struct mModelingData { /// ExaGeoStatData object containing needed descriptors, and locations. - std::unique_ptr> *mpData; + std::unique_ptr> *mpData; /// Configurations object containing user input data. - configurations::Configurations *mpConfiguration; + Configurations *mpConfiguration; /// Hardware configuration for the ExaGeoStat solver. - const hardware::ExaGeoStatHardware *mpHardware; + const ExaGeoStatHardware *mpHardware; /// Used Kernel for ExaGeoStat Modeling Data. const kernels::Kernel *mpKernel; @@ -36,8 +36,8 @@ namespace exageostat::dataunits { * @param aHardware The hardware configuration object. * @param aKernel The Kernel object. */ - mModelingData(std::unique_ptr> &aData, configurations::Configurations &aConfiguration, - const hardware::ExaGeoStatHardware &aHardware, T &aMatrix, const kernels::Kernel &aKernel) : + mModelingData(std::unique_ptr> &aData, Configurations &aConfiguration, + const ExaGeoStatHardware &aHardware, T &aMatrix, const kernels::Kernel &aKernel) : mpData(std::move(&aData)), mpConfiguration(&aConfiguration), mpHardware(&aHardware), mpMeasurementsMatrix(&aMatrix), mpKernel(&aKernel) {} }; diff --git a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp index bfc85a6e..48f3de04 100644 --- a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp +++ b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStatDescriptor.hpp * @brief Class for creating matrix descriptors used in CHAMELEON and HiCMA libraries. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-16 diff --git a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp index 0f0f5560..35be4d45 100644 --- a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp @@ -6,7 +6,7 @@ /** * @file ChameleonDescriptor.hpp * @brief Defines the ChameleonDescriptor class for creating matrix descriptors using the CHAMELEON library. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp index d3513252..9a34b820 100644 --- a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp @@ -6,7 +6,7 @@ /** * @file HicmaDescriptor.hpp * @brief Defines the Hicma Descriptor class for creating matrix descriptors using the HICMA library. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index 38e8d239..4f7af247 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -6,10 +6,10 @@ /** * @file ExaGeoStatHardware.hpp * @brief Contains the definition of the ExaGeoStatHardware class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-08-07 + * @date 2023-01-20 **/ #ifndef EXAGEOSTATCPP_EXAGEOSTATHARDWARE_HPP @@ -17,35 +17,48 @@ #include -namespace exageostat::hardware { +/** + * @brief Class representing the hardware configuration for the ExaGeoStat solver. + */ +class ExaGeoStatHardware { + +public: + /** + * @brief Constructor for ExaGeoStatHardware. + * @param[in] aComputation The computation mode for the solver. + * @param[in] aCoreNumber The number of CPU cores to use for the solver. + * @param[in] aGpuNumber The number of GPUs to use for the solver. + * + */ + ExaGeoStatHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); + + /** + * @brief Constructor for ExaGeoStatHardware. + * @param[in] aComputation The computation mode for the solver as a string. + * @param[in] aCoreNumber The number of CPU cores to use for the solver. + * @param[in] aGpuNumber The number of GPUs to use for the solver. + */ + ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber); + + /** + * @brief Destructor for ExaGeoStatHardware. + */ + virtual ~ExaGeoStatHardware(); /** - * @brief Class representing the hardware configuration for the ExaGeoStat solver. + * @brief Initializes hardware configuration. + * @param[in] aComputation The computation mode for the solver. + * @param[in] aCoreNumber The number of CPU cores to use for the solver. + * @param[in] aGpuNumber The number of GPUs to use for the solver. */ - class ExaGeoStatHardware { - - public: - /** - * @brief Constructor for ExaGeoStatHardware. - * @param[in] aComputation The computation mode for the solver. - * @param[in] aCoreNumber The number of CPU cores to use for the solver. - * @param[in] aGpuNumber The number of GPUs to use for the solver. - * - */ - ExaGeoStatHardware(const common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); - ExaGeoStatHardware(const int &aCoreNumber, const int &aGpuNumber); - - /** - * @brief Destructor for ExaGeoStatHardware. - */ - virtual ~ExaGeoStatHardware(); - - /** - * @brief Get the Chameleon hardware context. - * @return Pointer to the hardware context. - * - */ - [[nodiscard]] void *GetChameleonContext() const; + void InitHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); + + /** + * @brief Get the Chameleon hardware context. + * @return Pointer to the hardware context. + * + */ + [[nodiscard]] void *GetChameleonContext() const; #ifdef USE_HICMA @@ -54,28 +67,23 @@ namespace exageostat::hardware { * @return Pointer to the hardware context. * */ - [[nodiscard]] void *GetHicmaContext() const; + [[nodiscard]] void *GetHicmaContext() const; #endif - /** - * @brief Get the hardware context. - * @param[in] aComputation Used computation to decide whether to use Hicma or Chameleon context. - * @return Pointer to the hardware context. - * - */ - [[nodiscard]] void *GetContext(common::Computation aComputation) const; - - private: - //// Used Pointer to the Chameleon hardware context. - void *mpChameleonContext = nullptr; -#ifdef USE_HICMA - //// Used Pointer to the Hicma hardware context. - void *mpHicmaContext = nullptr; -#endif - //// Used Computation mode for the solver. - common::Computation mComputation; - }; -} // namespace exageostat + /** + * @brief Get the hardware context. + * @param[in] aComputation Used computation to decide whether to use Hicma or Chameleon context. + * @return Pointer to the hardware context. + * + */ + [[nodiscard]] void *GetContext(exageostat::common::Computation aComputation) const; + +private: + //// Used Pointer to the Chameleon hardware context. + void *mpChameleonContext = nullptr; + //// Used Pointer to the Hicma hardware context. + void *mpHicmaContext = nullptr; +}; #endif // EXAGEOSTATCPP_EXAGEOSTATHARDWARE_HPP \ No newline at end of file diff --git a/inst/include/helpers/CommunicatorMPI.hpp b/inst/include/helpers/CommunicatorMPI.hpp index d9051cbb..9e4160ea 100644 --- a/inst/include/helpers/CommunicatorMPI.hpp +++ b/inst/include/helpers/CommunicatorMPI.hpp @@ -6,7 +6,7 @@ /** * @file CommunicatorMPI.hpp * @brief Defines the CommunicatorMPI class for MPI rank communication. - * @version 1.0.0 + * @version 1.1.0 * @author Sameh Abdulah * @date 2023-11-10 **/ diff --git a/inst/include/helpers/DiskWriter.hpp b/inst/include/helpers/DiskWriter.hpp index 822e86a4..32e7715a 100644 --- a/inst/include/helpers/DiskWriter.hpp +++ b/inst/include/helpers/DiskWriter.hpp @@ -6,7 +6,7 @@ /** * @file DiskWriter.hpp * @brief Contains the definition of the DiskWriter class for writing data to disk. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/inst/include/helpers/DistanceCalculationHelpers.hpp b/inst/include/helpers/DistanceCalculationHelpers.hpp index b297be6f..e035995a 100644 --- a/inst/include/helpers/DistanceCalculationHelpers.hpp +++ b/inst/include/helpers/DistanceCalculationHelpers.hpp @@ -6,7 +6,7 @@ /** * @file DistanceCalculationHelpers.hpp * @brief Contains the definition of the DistanceCalculationHelpers class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/inst/include/kernels/Kernel.hpp b/inst/include/kernels/Kernel.hpp index 685b1d19..8e7043d9 100644 --- a/inst/include/kernels/Kernel.hpp +++ b/inst/include/kernels/Kernel.hpp @@ -7,7 +7,7 @@ /** * @file Kernels.hpp * @brief Header file for the Kernels class, which contains the main kernel functions. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/BivariateMaternFlexible.hpp b/inst/include/kernels/concrete/BivariateMaternFlexible.hpp index c8286aea..e2688090 100644 --- a/inst/include/kernels/concrete/BivariateMaternFlexible.hpp +++ b/inst/include/kernels/concrete/BivariateMaternFlexible.hpp @@ -6,7 +6,7 @@ /** * @file BivariateMaternFlexible.hpp * @brief Defines the BivariateMaternFlexible class, a Bivariate Matern Flexible kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp b/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp index ae702c31..05226389 100644 --- a/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp +++ b/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp @@ -6,7 +6,7 @@ /** * @file BivariateMaternParsimonious.hpp * @brief Defines the BivariateMaternParsimonious class, a Bivariate Matern Parsimonious kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp b/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp index a6b0416a..9a0b8dc0 100644 --- a/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp +++ b/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp @@ -6,7 +6,7 @@ /** * @file BivariateSpacetimeMaternStationary.hpp * @brief Defines the BivariateSpacetimeMaternStationary class, a Bivariate Spacetime Matern Stationary kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp b/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp index c81bd704..4aa525c8 100644 --- a/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp +++ b/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp @@ -6,7 +6,7 @@ /** * @file TrivariateMaternParsimonious.hpp * @brief Defines the TrivariateMaternParsimonious class, a Trivariate Matern Parsimonious kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp b/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp index 3248cfed..3afaf163 100644 --- a/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp +++ b/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateExpNonGaussian.hpp * @brief Defines the UnivariateExpNonGaussian class, a Univariate Exp Non Gaussian kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp index b1a6a95f..3866b690 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDbeta.hpp * @brief Defines the UnivariateMaternDbeta class, a Univariate Matern Dbeta kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp index a012eb87..6ac9e051 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdbetaBeta.hpp * @brief Defines the UnivariateMaternDdbetaBeta class, a Univariate Matern Ddbeta Beta kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp index 689e37ee..ae53d4c6 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdbetaNu.hpp * @brief Defines the UnivariateMaternDdbetaNu class, a Univariate Matern Ddbeta Nu kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp index 5623debd..29678d14 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdnuNu.hpp * @brief Defines the UnivariateMaternDdnuNu class, a Univariate Matern Ddnu Nu kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp index 162bee37..619705bc 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquare.hpp * @brief Defines the UnivariateMaternDdsigmaSquare class, a univariate stationary Matern kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp index 57f622fc..37606bf7 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquareBeta.hpp * @brief Defines the UnivariateMaternDdsigmaSquareBeta class, a Univariate Matern Ddsigma Square Beta kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp index ae33506a..c443b001 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquareNu.hpp * @brief Defines the UnivariateMaternDdsigmaSquareNu class, a Univariate Matern Ddsigma Square Nu kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDnu.hpp b/inst/include/kernels/concrete/UnivariateMaternDnu.hpp index f9eefb5a..d10ba4e0 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDnu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDnu.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDnu.hpp * @brief Defines the UnivariateMaternDnu class, a Univariate Matern Dnu kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp b/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp index 1da69311..eade1268 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDsigmaSquare.hpp * @brief Defines the UnivariateMaternDsigmaSquare class, a Univariate Matern Dsigma Square kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp b/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp index 1ee3a74e..b2e638af 100644 --- a/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternNonGaussian.hpp * @brief Defines the UnivariateMaternNonGaussian class, a Univariate Matern Non Gaussian kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp index 86e6cae9..52febd31 100644 --- a/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternNuggetsStationary.hpp * @brief Defines the UnivariateMaternNuggetsStationary class, a Univariate Matern Nuggets Stationary kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternStationary.hpp index ceb8cc7c..18040d86 100644 --- a/inst/include/kernels/concrete/UnivariateMaternStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternStationary.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternStationary.hpp * @brief Defines the UnivariateMaternStationary class, a univariate stationary Matern kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp b/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp index 35b52842..6ea8d77a 100644 --- a/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateSpacetimeMaternStationary.hpp * @brief Defines the UnivariateSpacetimeMaternStationary class, a Univariate Spacetime Matern Stationary kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp index 68bf6729..cc42108f 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp @@ -6,7 +6,7 @@ /** * @file LinearAlgebraFactory.hpp * @brief Header file for the LinearAlgebraFactory class, which creates linear algebra solvers based on the input computation type. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-03-20 **/ diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp index 679acf12..2a0f66da 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp @@ -6,7 +6,7 @@ /** * @file LinearAlgebraMethods.hpp * @brief Header file for the LinearAlgebraMethods class, which defines the interface for linear algebra solvers. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 @@ -25,7 +25,7 @@ extern "C" { #include } -#include +#include #include #include #include @@ -59,7 +59,7 @@ namespace exageostat::linearAlgebra { * @return void * */ - void InitiateDescriptors(configurations::Configurations &aConfigurations, + void InitiateDescriptors(Configurations &aConfigurations, dataunits::DescriptorData &aDescriptorData, const int &aP, T *apMeasurementsMatrix = nullptr); @@ -68,7 +68,7 @@ namespace exageostat::linearAlgebra { * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in,out] aDescriptorData Descriptor Data object to be populated with descriptors and data. */ - void InitiateFisherDescriptors(configurations::Configurations &aConfigurations, + void InitiateFisherDescriptors(Configurations &aConfigurations, dataunits::DescriptorData &aDescriptorData); /** @@ -80,7 +80,7 @@ namespace exageostat::linearAlgebra { * @return void * */ - void InitiatePredictionDescriptors(configurations::Configurations &aConfigurations, + void InitiatePredictionDescriptors(Configurations &aConfigurations, std::unique_ptr> &aData, const int &aP); /** @@ -92,7 +92,7 @@ namespace exageostat::linearAlgebra { * @return void * */ - void InitiateMLOEMMOMDescriptors(configurations::Configurations &aConfigurations, + void InitiateMLOEMMOMDescriptors(Configurations &aConfigurations, std::unique_ptr> &aData, const int &aP); /** @@ -104,8 +104,8 @@ namespace exageostat::linearAlgebra { * @return None. * */ - void GenerateSyntheticData(configurations::Configurations &aConfigurations, - const hardware::ExaGeoStatHardware &aHardware, + void GenerateSyntheticData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, const kernels::Kernel &aKernel); @@ -152,7 +152,7 @@ namespace exageostat::linearAlgebra { * */ void - GenerateObservationsVector(configurations::Configurations &aConfigurations, + GenerateObservationsVector(Configurations &aConfigurations, std::unique_ptr> &aData, dataunits::Locations *apLocation1, dataunits::Locations *apLocation2, dataunits::Locations *apLocation3, const int &aDistanceMetric, @@ -169,9 +169,9 @@ namespace exageostat::linearAlgebra { * @return log likelihood value * */ - virtual T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, + virtual T ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, - configurations::Configurations &aConfigurations, const double *apTheta, + Configurations &aConfigurations, const double *apTheta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) = 0; /** @@ -335,8 +335,8 @@ namespace exageostat::linearAlgebra { T *ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfiguration, + const ExaGeoStatHardware &aHardware, + Configurations &aConfiguration, exageostat::dataunits::Locations &aMissLocations, exageostat::dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); @@ -360,8 +360,8 @@ namespace exageostat::linearAlgebra { T *ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfiguration, + const ExaGeoStatHardware &aHardware, + Configurations &aConfiguration, exageostat::dataunits::Locations &aMissLocations, exageostat::dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); @@ -418,7 +418,7 @@ namespace exageostat::linearAlgebra { * @param[in] aDescData Descriptor data containing required Z matrix Descriptor. * @param[in] aP the P value of the kernel multiplied by time slot. */ - void ExaGeoStatGetZObs(exageostat::configurations::Configurations &aConfigurations, T *apZ, const int &aSize, + void ExaGeoStatGetZObs(Configurations &aConfigurations, T *apZ, const int &aSize, exageostat::dataunits::DescriptorData &aDescData, T *apMeasurementsMatrix, const int &aP); /** @@ -435,9 +435,9 @@ namespace exageostat::linearAlgebra { * @param[in] aKernel Reference to the kernel object to use. * @return void */ - void ExaGeoStatMLETileMLOEMMOM(exageostat::configurations::Configurations &aConfigurations, + void ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigurations, std::unique_ptr> &aData, - const exageostat::hardware::ExaGeoStatHardware &aHardware, T *apTruthTheta, + const ExaGeoStatHardware &aHardware, T *apTruthTheta, T *apEstimatedTheta, dataunits::Locations &aMissLocations, dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); @@ -451,9 +451,9 @@ namespace exageostat::linearAlgebra { * @return Fisher Matrix */ T * - ExaGeoStatFisherTile(configurations::Configurations &aConfigurations, + ExaGeoStatFisherTile(Configurations &aConfigurations, std::unique_ptr> &aData, - const hardware::ExaGeoStatHardware &aHardware, T *apTheta, + const ExaGeoStatHardware &aHardware, T *apTheta, const kernels::Kernel &aKernel); /** diff --git a/inst/include/linear-algebra-solvers/concrete/ChameleonHeaders.hpp b/inst/include/linear-algebra-solvers/concrete/ChameleonHeaders.hpp index d851e510..ece6a56a 100644 --- a/inst/include/linear-algebra-solvers/concrete/ChameleonHeaders.hpp +++ b/inst/include/linear-algebra-solvers/concrete/ChameleonHeaders.hpp @@ -1,7 +1,7 @@ /** * @file ChameleonHeaders.hpp * @brief This file contains the necessary includes for using the Chameleon library. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-08-24 **/ diff --git a/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp b/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp index 9d216bbe..2093821b 100644 --- a/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp +++ b/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp @@ -1,7 +1,7 @@ /** * @file HicmaHeaders.hpp * @brief This file contains the necessary includes for using the Chameleon library. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-08-24 **/ diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp index 6c2d5c61..32b1c86d 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp @@ -7,7 +7,7 @@ * @file ChameleonImplementation.hpp * @brief This file contains the declaration of ChameleonImplementation class. * @details ChameleonImplementation is a concrete implementation of the LinearAlgebraMethods class for the common functionality implementation shared between dense and diagonal-super tile matrices. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 @@ -33,8 +33,8 @@ namespace exageostat::linearAlgebra { * @brief Calculates the log likelihood value of a given value theta. * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() */ - T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, - configurations::Configurations &aConfigurations, const double *theta, + T ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, + Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) override; /** diff --git a/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp index 3f6dcf93..9f8a8600 100644 --- a/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp @@ -46,15 +46,15 @@ namespace exageostat::linearAlgebra::tileLowRank { * @param[in] aP the P value of the kernel multiplied by time slot. */ void SetModelingDescriptors(std::unique_ptr> &aData, - configurations::Configurations &aConfigurations, const int &aP); + Configurations &aConfigurations, const int &aP); /** * @brief Calculates the log likelihood value of a given value theta. * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() */ - T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &apHardware, + T ExaGeoStatMLETile(const ExaGeoStatHardware &apHardware, std::unique_ptr> &aData, - configurations::Configurations &aConfigurations, const double *theta, + Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) override; /** diff --git a/inst/include/prediction/Prediction.hpp b/inst/include/prediction/Prediction.hpp index e96742bc..1e0f402e 100644 --- a/inst/include/prediction/Prediction.hpp +++ b/inst/include/prediction/Prediction.hpp @@ -6,7 +6,7 @@ /** * @file Prediction.hpp * @brief Contains the definition of the Prediction class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 @@ -48,9 +48,9 @@ namespace exageostat::prediction { * @param[in] aKernel Reference to the kernel object to use. * @return */ - void PredictMissingData(const exageostat::hardware::ExaGeoStatHardware &aHardware, + void PredictMissingData(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, - exageostat::configurations::Configurations &aConfigurations, T *apMeasurementsMatrix, + Configurations &aConfigurations, T *apMeasurementsMatrix, const kernels::Kernel &aKernel); /** @@ -66,7 +66,7 @@ namespace exageostat::prediction { * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ - void InitializePredictionArguments(exageostat::configurations::Configurations &aConfigurations, + void InitializePredictionArguments(Configurations &aConfigurations, std::unique_ptr> &aData, std::unique_ptr> &aLinearAlgebraSolver, T *apZObs, T *apZActual, exageostat::dataunits::Locations &aMissLocation, diff --git a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp index ad07828f..f0417219 100644 --- a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp +++ b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp @@ -6,7 +6,7 @@ /** * @file PredictionAuxiliaryFunctions.hpp * @brief Contains the definition of the PredictionAuxiliaryFunctions.hpp class. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/inst/include/prediction/PredictionHelpers.hpp b/inst/include/prediction/PredictionHelpers.hpp index 9a684390..50ab483c 100644 --- a/inst/include/prediction/PredictionHelpers.hpp +++ b/inst/include/prediction/PredictionHelpers.hpp @@ -6,7 +6,7 @@ /** * @file PredictionHelpers.hpp * @brief Contains the definition of the PredictionHelpers.hpp class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 @@ -41,7 +41,7 @@ namespace exageostat::prediction { * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ - static void PickRandomPoints(exageostat::configurations::Configurations &aConfigurations, + static void PickRandomPoints(Configurations &aConfigurations, std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, exageostat::dataunits::Locations &aMissLocation, exageostat::dataunits::Locations &aObsLocation, const int &aP); diff --git a/inst/include/results/Results.hpp b/inst/include/results/Results.hpp index b112284c..3da9e3a9 100644 --- a/inst/include/results/Results.hpp +++ b/inst/include/results/Results.hpp @@ -6,7 +6,7 @@ /** * @file Results.hpp * @brief Defines the Results class for storing and accessing result data. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-09-14 **/ diff --git a/inst/include/utilities/EnumStringParser.hpp b/inst/include/utilities/EnumStringParser.hpp new file mode 100644 index 00000000..69ddfe0d --- /dev/null +++ b/inst/include/utilities/EnumStringParser.hpp @@ -0,0 +1,66 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file EnumStringParser.hpp + * @brief Provides utility functions for parsing enumeration values from strings. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-20 +**/ + +#ifndef EXAGEOSTATCPP_ENUMSTRINGPARSER_HPP +#define EXAGEOSTATCPP_ENUMSTRINGPARSER_HPP + +#include + +#include +#include + +using namespace exageostat::common; + +/** + * @brief Convert a string representation of computation mode to its corresponding enum value. + * @param[in] aComputation String representation of computation mode. + * @return Computation enum value. + */ +inline Computation GetInputComputation(std::string aComputation) { + std::transform(aComputation.begin(), aComputation.end(), + aComputation.begin(), ::tolower); + + if (aComputation == "exact" || aComputation == "dense") { + return EXACT_DENSE; + } else if (aComputation == "dst" || aComputation == "diagonal_super_tile") { + return DIAGONAL_APPROX; + } else if (aComputation == "tlr" || aComputation == "tile_low_rank") { + return TILE_LOW_RANK; + } else { + const std::string msg = "Error in Initialization : Unknown computation Value" + std::string(aComputation); + throw API_EXCEPTION(msg, INVALID_ARGUMENT_ERROR); + } +} + +/** + * @brief Converts string to dimension enum. + * @param[in] aDimension Dimension as a string. + * @return Dimension as an enum. + */ +inline Dimension GetInputDimension(std::string aDimension) { + std::transform(aDimension.begin(), aDimension.end(), + aDimension.begin(), ::tolower); + + if (aDimension == "2D" || aDimension == "2d") { + return Dimension2D; + } else if (aDimension == "3D" || aDimension == "3d") { + return Dimension3D; + } else if (aDimension == "ST" || aDimension == "st") { + return DimensionST; + } else { + const std::string msg = "Error in Initialization : Unknown computation Value" + std::string(aDimension); + throw API_EXCEPTION(msg, INVALID_ARGUMENT_ERROR); + } +} + +#endif //EXAGEOSTATCPP_ENUMSTRINGPARSER_HPP diff --git a/inst/include/utilities/ErrorHandler.hpp b/inst/include/utilities/ErrorHandler.hpp new file mode 100644 index 00000000..5d225f0a --- /dev/null +++ b/inst/include/utilities/ErrorHandler.hpp @@ -0,0 +1,131 @@ +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ErrorHandler.hpp + * @version 1.1.0 + * @brief Provides error handling functionalities. + * @details Defines macros and functions for handling errors and warnings. + * @author Mahmoud ElKarargy + * @author David Helmy + * @date 2024-01-20 +**/ + +#ifndef EXAGEOSTATCPP_ERRORHANDLER_HPP +#define EXAGEOSTATCPP_ERRORHANDLER_HPP + +#include + +#ifdef USE_CUDA +#include +#endif + +/** + * @brief EXAGEOSTAT API Exceptions Macro to use for Errors and Warnings. + */ + #define API_EXCEPTION(MESSAGE, ERROR_TYPE) \ + APIException(MESSAGE, ERROR_TYPE) + +#ifdef USE_CUDA +/** + * @brief Useful macro wrapper for all cuda API calls to ensure correct returns, + * and error throwing on failures. + */ +#define GPU_ERROR_CHECK(ans) { APIException::AssertGPU((ans), __FILE__, __LINE__); } +#endif + +/** + * @brief Enumeration for error types. + */ +enum ErrorType : int { + RUNTIME_ERROR = 0, + RANGE_ERROR = 1, + INVALID_ARGUMENT_ERROR = 2, + WARNING = 3, +}; + +/** + * @class APIException + * @brief Custom exception class for handling API errors and warnings. + */ +class APIException : public std::exception{ + +public: + + /** + * @brief Constructor for APIException. + * @param[in] aMessage The error or warning message. + * @param[in] aErrorCode The error type. + */ + APIException(const std::string &aMessage, const ErrorType &aErrorCode) { + + if (aErrorCode != WARNING) { + APIException::ThrowError(aMessage, aErrorCode); + } else { + APIException::ThrowWarning(aMessage); + } + } + + /** + * @brief Destructor for APIException. + */ + ~APIException() override = default; + +#ifdef USE_CUDA + /** + * @brief Function to assert the return code of a CUDA API call and ensure it completed successfully. + * @param[in] aCode The code returned from the CUDA API call. + * @param[in] aFile The name of the file that the assertion was called from. + * @param[in] aLine The line number in the file that the assertion was called from. + */ + inline static void AssertGPU(cudaError_t aCode, const char *aFile, int aLine) + { + if (aCode != cudaSuccess) + { +#ifdef USING_R + std::string s="GPU Assert: "+std::string(cudaGetErrorString(aCode)); + Rcpp::stop(s); +#else + char s[200]; + sprintf((char*)s,"GPU Assert: %s %s %d\n", cudaGetErrorString(aCode), aFile, aLine); + throw std::invalid_argument(s); +#endif + } + } +#endif + + +private: + + /** + * @brief Helper function to throw error based on error type. + * @param[in] aString The error message. + * @param[in] aErrorCode The error type. + */ + static void ThrowError(const std::string &aString, const ErrorType &aErrorCode) { +#ifdef USING_R +// Rcpp::stop(aString); +#else + if (aErrorCode == RUNTIME_ERROR) { + throw std::runtime_error(aString); + } else if (aErrorCode == INVALID_ARGUMENT_ERROR) { + throw std::invalid_argument(aString); + } else if (aErrorCode == RANGE_ERROR) { + throw std::range_error(aString); + } +#endif + } + + /** + * @brief Helper function to throw warning. + * @param[in] aString The warning message. + */ + static void ThrowWarning(const std::string &aString) { +#ifdef USING_R +// Rcpp::warning(aString); +#endif + } +}; + +#endif //EXAGEOSTATCPP_ERRORHANDLER_HPP \ No newline at end of file diff --git a/inst/include/utilities/Logger.hpp b/inst/include/utilities/Logger.hpp new file mode 100644 index 00000000..967b039b --- /dev/null +++ b/inst/include/utilities/Logger.hpp @@ -0,0 +1,132 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file Logger.hpp + * @brief Provides logging and timing macros for debugging and profiling. + * @details Defines macros for verbose logging, various levels of logging, and timing. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-04 +**/ + +#ifndef EXAGEOSTATCPP_LOGGER_HPP +#define EXAGEOSTATCPP_LOGGER_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * @def DEFAULT_PRECISION + * @brief The value of the default C++ std::cout number of precision. + */ +#define DEFAULT_PRECISION 6 + +/** + * @def VERBOSE(msg) + * @brief Verbose macro for logging and debugging mode. + */ +#define VERBOSE(msg) \ + if(Configurations::GetVerbosity() == exageostat::common::Verbose::DETAILED_MODE && \ + exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) { \ + std::ostringstream oss; \ + oss << "\t\t\t " << msg << std::endl; \ + EXAGEOSTAT_PRINTER(oss.str()); \ + } + +/** + * @def LOGGER_1(msg) + * @brief LOGGER_1 macro for logging outputs with double taps and new line at the end. + */ +#define LOGGER_1(msg) \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + std::ostringstream oss; \ + oss << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg << std::endl; \ + EXAGEOSTAT_PRINTER(oss.str()); \ + } + +/** + * @def LOGGER_2(msg, A) + * @brief LOGGER_2 macro for logging outputs with double taps and without new line at the end. + */ +#define LOGGER_2(msg, A) \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + std::ostringstream oss; \ + oss << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; \ + EXAGEOSTAT_PRINTER(oss.str()); \ +} +/** + * @def LOGGER_CONTROL(x, A, B, FUNC, ...) + * @brief LOGGER_CONTROL is The internal macro that simply strips the excess and ends up with the required macro + */ +#define LOGGER_CONTROL(x, A, B, FUNC, ...) FUNC + +/** + * @def LOGGER(...) + * @brief LOGGER macro that's called, Used to logging outputs. + */ +#define LOGGER(...) LOGGER_CONTROL(,##__VA_ARGS__, \ + LOGGER_2(__VA_ARGS__), \ + LOGGER_1(__VA_ARGS__), \ + ) + +/** + * @def LOGGER_PRECISION_1(msg, precision) + * @brief LOGGER_PRECISION_1 macro for logging outputs without any taps, without new line at the end, and with customized precision. + */ +#define LOGGER_PRECISION_1(msg, precision) \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + std::ostringstream oss; \ + oss << std::fixed << std::setprecision(precision) << msg; \ + EXAGEOSTAT_PRINTER(oss.str()); \ + } + +/** + * @def LOGGER_PRECISION_2(msg) + * @brief LOGGER_PRECISION_2 macro for logging outputs without any taps, without new line at the end, and with default C++ precision. + */ +#define LOGGER_PRECISION_2(msg) \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) {\ + std::ostringstream oss; \ + oss << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; \ + EXAGEOSTAT_PRINTER(oss.str()); \ + } +/** + * @def LOGGER_PRECISION_CONTROL + * @brief is The internal macro that simply strips the excess and ends up with the required macro + */ +#define LOGGER_PRECISION_CONTROL(x, A, B, FUNC, ...) FUNC + +/** + * @def LOGGER_PRECISION(...) + * @brief LOGGER_PRECISION macro that's called, Used for logging outputs with precision. + */ +#define LOGGER_PRECISION(...) LOGGER_PRECISION_CONTROL(,##__VA_ARGS__, \ + LOGGER_PRECISION_1(__VA_ARGS__), \ + LOGGER_PRECISION_2(__VA_ARGS__), \ + ) + +/** + * @def START_TIMING(t) + * @brief Timing macro to start timing. + */ +#define START_TIMING(t) auto t##_start = std::chrono::high_resolution_clock::now() + +/** + * @def STOP_TIMING(t) + * @brief Timing macro to stop timing. + */ +#define STOP_TIMING(t) auto t##_end = std::chrono::high_resolution_clock::now(); \ + t = std::chrono::duration_cast>(t##_end - t##_start).count() + +#endif //EXAGEOSTATCPP_LOGGER_HPP diff --git a/inst/include/utilities/Printer.hpp b/inst/include/utilities/Printer.hpp new file mode 100644 index 00000000..1185e936 --- /dev/null +++ b/inst/include/utilities/Printer.hpp @@ -0,0 +1,38 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file Printer.hpp + * @version 1.1.0 + * @brief Provides printing functionality for output messages. + * @details Defines macros for printing messages to the console or R output. + * @author Mahmoud ElKarargy + * @author David Helmy + * @date 2024-01-20 +**/ + +#ifndef EXAGEOSTATCPP_PRINTER_HPP +#define EXAGEOSTATCPP_PRINTER_HPP + +#include +#ifdef USING_R +//#include +#endif + +/** + * @def EXAGEOSTAT_PRINTER(message) + * @brief Macro for printing messages. + * @details Depending on whether the code is compiled with Rcpp support, it prints the message to the console or R output. + */ +#ifdef USING_R +#define EXAGEOSTAT_PRINTER(message) \ + std::cout <<(message); +// Rcpp::Rcout<<(message); +#else +#define EXAGEOSTAT_PRINTER(message) \ + std::cout<<(message); +#endif + +#endif //EXAGEOSTATCPP_PRINTER_HPP diff --git a/scripts/Benchmarking.sh b/scripts/Benchmarking.sh index 1bd12b0b..a21c9ae3 100644 --- a/scripts/Benchmarking.sh +++ b/scripts/Benchmarking.sh @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file config.sh -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-10-10 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a00e3c2..13d34380 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,10 +5,10 @@ # @file CMakeLists.txt # @brief This file contains the CMake configuration for the ExaGeoStat library. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah -# @date 2023-01-30 +# @date 2024-02-04 # Add subdirectories for configurations, data-generators, data-units, and linear-algebra-solvers. add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/configurations) @@ -21,15 +21,9 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/helpers) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/hardware) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/prediction) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/results) -#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/adapters) if (USE_R) - set( - SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/DataTypeModule.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/RcppExports.cpp - ${SOURCES} - ) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Rcpp-adapters) endif () # Set the name of the library to be created. diff --git a/src/DataTypeModule.cpp b/src/DataTypeModule.cpp deleted file mode 100644 index 70da2481..00000000 --- a/src/DataTypeModule.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2023, King Abdullah University of Science and Technology - * All rights reserved. - * - * MPCR is an R package provided by the STSDS group at KAUST - * - **/ -#include -#include - -/** Expose C++ class to R to be able to use Wrap and As - * Allows C++ to Send and Receive Class object from R - **/ -RCPP_EXPOSED_CLASS(ExaGeoStatHardware) - -/** Expose C++ Object With the Given functions **/ -RCPP_MODULE(ExaGeoStatCPP) { - - /** MPCR Class **/ - using namespace Rcpp; - - - /** Basic Utilities **/ - class_ ("Hardware") - .constructor (); - - /** Function that are not masked **/ - -} diff --git a/src/adapters/CMakeLists.txt b/src/Rcpp-adapters/CMakeLists.txt similarity index 65% rename from src/adapters/CMakeLists.txt rename to src/Rcpp-adapters/CMakeLists.txt index 1248e066..50006846 100644 --- a/src/adapters/CMakeLists.txt +++ b/src/Rcpp-adapters/CMakeLists.txt @@ -5,13 +5,13 @@ # @file CMakeLists.txt # @version 1.1.0 -# @brief CMake build script for the adapters library. # @author Mahmoud ElKarargy -# @date 2023-01-16 +# @date 2024-01-29 -# Define the sources for the library set(SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/HardwareAdapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/FunctionsAdapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/RcppExports.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/RcppModules.cpp ${SOURCES} PARENT_SCOPE ) \ No newline at end of file diff --git a/src/Rcpp-adapters/FunctionsAdapter.cpp b/src/Rcpp-adapters/FunctionsAdapter.cpp new file mode 100644 index 00000000..5bb330d8 --- /dev/null +++ b/src/Rcpp-adapters/FunctionsAdapter.cpp @@ -0,0 +1,120 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file FunctionsAdapter.cpp + * @brief Header file for function adapters in the ExaGeoStat software. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-29 +**/ + +#include + +#include +#include +#include + +using namespace std; + +using namespace exageostat::dataunits; +using namespace exageostat::api; + +namespace exageostat::adapters { + + Configurations * + R_InitializeArguments(const int &aProblemSize, const string &aKernelName, const vector &aTileSize, + const vector &aP_QGrid, const int &aTimeSlot, const string &aComputation, + const string &aPrecision, const vector &aCoresGPUsNumber, const int &aBand, + const int &aMaxRank, const vector &aInitialTheta, + const vector > &aLowerUpperBounds, const vector &aEstimatedTheta, + const string &aVerbose, const string &aDimension, const int &aMaxMleIterations, + const double &aTolerance, const vector &aPrediction) { + + vector argStrings; + + // Program name or placeholder + argStrings.emplace_back("--ConfigurationsModuleR"); + // Convert each argument to string and add to argv + argStrings.push_back("--N=" + to_string(aProblemSize)); + argStrings.push_back("--kernel=" + aKernelName); + argStrings.push_back("--dts=" + to_string(aTileSize[0])); + argStrings.push_back("--lts=" + to_string(aTileSize[1])); + argStrings.push_back("--p=" + to_string(aP_QGrid[0])); + argStrings.push_back("--q=" + to_string(aP_QGrid[1])); + argStrings.push_back("--time_slot=" + to_string(aTimeSlot)); + argStrings.push_back("--computation=" + aComputation); + argStrings.push_back("--precision=" + aPrecision); + argStrings.push_back("--cores=" + to_string(aCoresGPUsNumber[0])); + argStrings.push_back("--gpus=" + to_string(aCoresGPUsNumber[1])); + argStrings.push_back("--band=" + to_string(aBand)); + argStrings.push_back("--max_rank=" + to_string(aMaxRank)); + argStrings.push_back("--iTheta=" + to_string(aInitialTheta[0]) + ":" + to_string(aInitialTheta[1]) + ":" + + to_string(aInitialTheta[2])); + argStrings.push_back("--lb=" + to_string(aLowerUpperBounds[0][0]) + ":" + to_string(aLowerUpperBounds[0][1]) + ":" + + to_string(aLowerUpperBounds[0][2])); + argStrings.push_back("--ub=" + to_string(aLowerUpperBounds[1][0]) + ":" + to_string(aLowerUpperBounds[1][1]) + ":" + + to_string(aLowerUpperBounds[1][2])); + argStrings.push_back("--eTheta=" + to_string(aEstimatedTheta[0]) + ":" + to_string(aEstimatedTheta[1]) + ":" + + to_string(aEstimatedTheta[2])); + argStrings.push_back("--verbose=" + aVerbose); + argStrings.push_back("--dimension=" + aDimension); + argStrings.push_back("--max_mle_iterations=" + to_string(aMaxMleIterations)); + argStrings.push_back("--tolerance=" + to_string(aTolerance)); + + // This means that ZMiss > 0, which means prediction is activated + if (aPrediction[0] > 0) { + argStrings.push_back("--ZMiss=" + to_string(aPrediction[0])); + + // if mspe is activated + if (aPrediction[1]) { + argStrings.emplace_back("--mspe"); + } + + // if idw is activated + if (aPrediction[2]) { + argStrings.emplace_back("--idw"); + } + + // if fisher is activated + if (aPrediction[3]) { + argStrings.emplace_back("--fisher"); + } + + // if mloe_mmom is activated + if (aPrediction[4]) { + argStrings.emplace_back("--mloe_mmom"); + } + } + // Now, allocate a char** array and copy the arguments into it. + // Argv ownership is passed to Configurations, and it's responsibility for freeing it. + char **argv = new char*[argStrings.size()]; + + for (size_t i = 0; i < argStrings.size(); ++i) { + argv[i] = new char[argStrings[i].size() + 1]; // +1 for the null terminator + std::strcpy(argv[i], argStrings[i].c_str()); + } + + auto configurations = new Configurations(); + configurations->InitializeArguments(argStrings.size(), argv); + return configurations; + } + + void R_ExaGeoStatAPI(ExaGeoStatHardware *apHardware, Configurations *apConfigurations) { + + if(apConfigurations->GetPrecision() == exageostat::common::DOUBLE){ + std::unique_ptr> data; + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(*apHardware, *apConfigurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(*apHardware, *apConfigurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(*apHardware, *apConfigurations, data); + } + else if(apConfigurations->GetPrecision() == exageostat::common::SINGLE){ + std::unique_ptr> data; + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(*apHardware, *apConfigurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(*apHardware, *apConfigurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(*apHardware, *apConfigurations, data); + } + } +} \ No newline at end of file diff --git a/src/Rcpp-adapters/RcppExports.cpp b/src/Rcpp-adapters/RcppExports.cpp new file mode 100644 index 00000000..97c0e9e0 --- /dev/null +++ b/src/Rcpp-adapters/RcppExports.cpp @@ -0,0 +1,45 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file RcppExports.cpp + * @brief Rcpp export definitions for ExaGeoStatCPP module. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author David Helmy + * @date 2023-01-20 +**/ + +#include + +using namespace Rcpp; + +#ifdef RCPP_USE_GLOBAL_ROSTREAM +Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); +Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); +#endif + +/** + * @brief Rcpp module boot function for ExaGeoStatCPP. + * @return An SEXP representing the Rcpp module. + */ +RcppExport SEXP _rcpp_module_boot_ExaGeoStatCPP(); + +/** + * @brief Array of R function call entries. + */ +static const R_CallMethodDef CallEntries[] = { + {"_rcpp_module_boot_ExaGeoStatCPP", (DL_FUNC) &_rcpp_module_boot_ExaGeoStatCPP, 0}, + {nullptr, nullptr, 0} +}; + +/** + * @brief R initialization function for ExaGeoStatCPP module. + * @param dll The DllInfo structure. + */ +RcppExport void R_init_ExaGeoStatCPP(DllInfo *dll) { + R_registerRoutines(dll, nullptr, CallEntries, nullptr, nullptr); + R_useDynamicSymbols(dll, FALSE); +} diff --git a/src/Rcpp-adapters/RcppModules.cpp b/src/Rcpp-adapters/RcppModules.cpp new file mode 100644 index 00000000..9732a0f5 --- /dev/null +++ b/src/Rcpp-adapters/RcppModules.cpp @@ -0,0 +1,51 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file RcppModules.cpp + * @brief Rcpp module definitions for ExaGeoStatCPP. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author David Helmy + * @date 2023-01-20 +**/ + +#include + +#include +#include +#include + +/** Expose C++ class to R to be able to use Wrap and As + * Allows C++ to Send and Receive Class object from R + **/ + +RCPP_EXPOSED_CLASS(ExaGeoStatHardware) +RCPP_EXPOSED_CLASS(Configurations) + +/** Expose C++ Object With the Given functions **/ +RCPP_MODULE(ExaGeoStatCPP) { + + /** ExaGeoStatCPP Class **/ + using namespace Rcpp; + + /** Hardware Class **/ + class_("Hardware") + .constructor(); + + /** Configurations Class **/ + class_("Configurations") + .constructor(); + + /** Configurations Function **/ + function("configurations_init", &exageostat::adapters::R_InitializeArguments, + List::create(_["n"], _["kernel"], _["tile_size"], _["p_q"] = IntegerVector::create(1, 1), _["time_slot"] = 1, + _["computation"] = "exact", _["precision"] = "double", _["cores_gpus"] = IntegerVector::create(1, 0), + _["band"] = 0, _["max_rank"] = 1, _["iTheta"], _["lb_ub"], + _["eTheta"]=IntegerVector::create(-1,-1,-1), _["verbose"] = "standard", _["dimension"] = "2D", + _["mle_itr"], _["tol"] = 4, _["prediction"] = IntegerVector::create(0, 0, 0, 0, 0))); + + function("exageostat", &exageostat::adapters::R_ExaGeoStatAPI, List::create(_["hardware"], _["config"])); +} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp deleted file mode 100644 index b3f0752c..00000000 --- a/src/RcppExports.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2023, King Abdullah University of Science and Technology - * All rights reserved. - * - * MPCR is an R package provided by the STSDS group at KAUST - * - **/ - -#include - - -using namespace Rcpp; - -#ifdef RCPP_USE_GLOBAL_ROSTREAM -Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); -Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); -#endif - - -RcppExport SEXP _rcpp_module_boot_ExaGeoStatCPP(); - -static const R_CallMethodDef CallEntries[] = { - {"_rcpp_module_boot_ExaGeoStatCPP", (DL_FUNC) &_rcpp_module_boot_ExaGeoStatCPP, 0}, - {NULL, NULL, 0} -}; - -RcppExport void R_init_ExaGeoStatCPP(DllInfo *dll) { - R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); - R_useDynamicSymbols(dll, FALSE); -} diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index 9a434188..1c2960e6 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @brief CMake build script for the operators library, which includes the OperatorMethods base class and the OperatorFactory class for creating operators of different types. # @author Mahmoud ElKarargy # @date 2023-05-30 diff --git a/src/api/ExaGeoStat.cpp b/src/api/ExaGeoStat.cpp index d8a7d24a..2d4cbb76 100644 --- a/src/api/ExaGeoStat.cpp +++ b/src/api/ExaGeoStat.cpp @@ -6,9 +6,9 @@ /** * @file ExaGeoStat.cpp * @brief High-Level Wrapper class containing the static API for ExaGeoStat operations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-05-30 + * @date 2024-02-04 **/ #include @@ -20,15 +20,13 @@ using namespace std; using namespace nlopt; using namespace exageostat::api; -using namespace exageostat::configurations; using namespace exageostat::generators; using namespace exageostat::dataunits; -using namespace exageostat::hardware; using namespace exageostat::prediction; template void ExaGeoStat::ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfigurations, + Configurations &aConfigurations, std::unique_ptr> &aData) { LOGGER("** ExaGeoStat data generation **") @@ -95,7 +93,7 @@ ExaGeoStat::ExaGeoStatMLETileAPI(const std::vector &aTheta, std::vect template void ExaGeoStat::ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, T *apMeasurementsMatrix) { LOGGER("** ExaGeoStat data Prediction **") diff --git a/src/configurations/CMakeLists.txt b/src/configurations/CMakeLists.txt index d0bee479..5193edb9 100644 --- a/src/configurations/CMakeLists.txt +++ b/src/configurations/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief This file contains the CMake configuration for the data-units directory. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-01-31 diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index b15e5541..d71658c4 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -6,19 +6,18 @@ /** * @file Configurations.cpp * @brief This file defines the Configurations class which stores the configuration parameters for ExaGeoStat. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-01-31 + * @date 2024-02-04 **/ #include #include -#include +#include using namespace std; -using namespace exageostat::configurations; using namespace exageostat::common; Verbose Configurations::mVerbosity = Verbose::STANDARD_MODE; @@ -27,7 +26,7 @@ bool Configurations::mIsThetaInit = false; Configurations::Configurations() { // Set default values for arguments! - SetComputation(common::EXACT_DENSE); + SetComputation(EXACT_DENSE); SetCoresNumber(1); SetGPUsNumbers(0); SetPGrid(1); @@ -35,7 +34,7 @@ Configurations::Configurations() { SetMaxRank(1); SetIsOOC(false); SetKernelName(""); - SetDimension(common::Dimension2D); + SetDimension(Dimension2D); SetTimeSlot(1); SetProblemSize(0); SetDenseTileSize(0); @@ -45,7 +44,6 @@ Configurations::Configurations() { SetBand(0); SetLoggerPath(""); SetIsSynthetic(true); - SetIsCSV(false); vector theta; SetInitialTheta(theta); SetLowerBounds(theta); @@ -54,21 +52,27 @@ Configurations::Configurations() { SetSeed(0); SetLogger(false); SetUnknownObservationsNb(0); - SetMeanSquareError(0.0); SetApproximationMode(1); SetActualObservationsFilePath(""); SetRecoveryFile(""); - SetPrecision(common::DOUBLE); + SetPrecision(DOUBLE); SetIsMSPE(false); SetIsFisher(false); SetIsIDW(false); SetIsMLOEMMOM(false); SetDataPath(""); - SetDistanceMetric(common::EUCLIDEAN_DISTANCE); + SetDistanceMetric(EUCLIDEAN_DISTANCE); SetAccuracy(0); SetIsNonGaussian(false); } +Configurations::~Configurations() { + + for (int i = 0; i < this->mArgC; ++i) { + delete[] this->mpArgV[i]; + } + delete[] this->mpArgV; +} void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { @@ -223,7 +227,7 @@ void Configurations::InitializeAllTheta() { if (!mIsThetaInit) { - int parameters_number = kernels::KernelsConfigurations::GetParametersNumberKernelMap()[this->GetKernelName()]; + int parameters_number = exageostat::kernels::KernelsConfigurations::GetParametersNumberKernelMap()[this->GetKernelName()]; InitTheta(GetInitialTheta(), parameters_number); SetInitialTheta(GetInitialTheta()); @@ -275,7 +279,6 @@ void Configurations::InitializeDataGenerationArguments() { argument_name == "--data_path") { SetDataPath(argument_value); SetIsSynthetic(false); - SetIsCSV(true); } } } @@ -694,4 +697,5 @@ void Configurations::PrintSummary() { int Configurations::CalculateZObsNumber() { return (this->GetProblemSize()) - this->GetUnknownObservationsNb(); -} \ No newline at end of file +} + diff --git a/src/data-generators/CMakeLists.txt b/src/data-generators/CMakeLists.txt index 67966b9e..47cc703f 100644 --- a/src/data-generators/CMakeLists.txt +++ b/src/data-generators/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief CMake configuration file for Data Generators module -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-02-14 diff --git a/src/data-generators/DataGenerator.cpp b/src/data-generators/DataGenerator.cpp index 548185a0..a667a221 100644 --- a/src/data-generators/DataGenerator.cpp +++ b/src/data-generators/DataGenerator.cpp @@ -6,9 +6,9 @@ /** * @file DataGenerator.cpp * @brief Implementation of DataGenerator class - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-02-14 + * @date 2024-02-04 **/ #include @@ -20,23 +20,19 @@ using namespace exageostat::generators; using namespace exageostat::generators::synthetic; using namespace exageostat::generators::csv; using namespace exageostat::dataunits; -using namespace exageostat::configurations; template std::unique_ptr> DataGenerator::CreateGenerator(Configurations &apConfigurations) { // Check the used Data generation method, whether it's synthetic or real. mIsSynthetic = apConfigurations.GetIsSynthetic(); - mIsCSV = apConfigurations.GetIsCSV(); results::Results::GetInstance()->SetIsSynthetic(mIsSynthetic); // Return DataGenerator unique pointer of Synthetic type if (mIsSynthetic) { return std::unique_ptr>(SyntheticGenerator::GetInstance()); - } else if (mIsCSV) { - return std::unique_ptr>(CSVDataGenerator::GetInstance()); } else { - throw std::runtime_error("Data Loading for this file type is unsupported for now"); + return std::unique_ptr>(CSVDataGenerator::GetInstance()); } } @@ -45,13 +41,9 @@ DataGenerator::~DataGenerator() { // Return DataGenerator unique pointer of Synthetic type if (mIsSynthetic) { SyntheticGenerator::GetInstance()->ReleaseInstance(); - } else if (mIsCSV) { - CSVDataGenerator::GetInstance()->ReleaseInstance(); } else { - std::cerr << "Data Loading for this file type is unsupported for now" << std::endl; - std::exit(1); + CSVDataGenerator::GetInstance()->ReleaseInstance(); } } template bool DataGenerator::mIsSynthetic = true; -template bool DataGenerator::mIsCSV = false; diff --git a/src/data-generators/concrete/CMakeLists.txt b/src/data-generators/concrete/CMakeLists.txt index 55d821bb..c10d57bf 100644 --- a/src/data-generators/concrete/CMakeLists.txt +++ b/src/data-generators/concrete/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief CMake configuration file for the Synthetic Generator module -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-02-14 diff --git a/src/data-generators/concrete/CSVDataGenerator.cpp b/src/data-generators/concrete/CSVDataGenerator.cpp index 8d14d5a1..7543d5bf 100644 --- a/src/data-generators/concrete/CSVDataGenerator.cpp +++ b/src/data-generators/concrete/CSVDataGenerator.cpp @@ -6,10 +6,10 @@ /** * @file CSVDataGenerator.cpp * @brief Implementation of the CSVDataGenerator class - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-02-14 + * @date 2024-02-04 **/ #include @@ -19,8 +19,6 @@ using namespace std; using namespace exageostat::generators::csv; -using namespace exageostat::configurations; -using namespace exageostat::hardware; using namespace exageostat::dataunits; using namespace exageostat::kernels; using namespace exageostat::common; @@ -37,8 +35,8 @@ CSVDataGenerator *CSVDataGenerator::GetInstance() { template unique_ptr> -CSVDataGenerator::CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, +CSVDataGenerator::CreateData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) { // create vectors that will be populated with read data. diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index e2de00a9..abd15eba 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -6,19 +6,17 @@ /** * @file SyntheticGenerator.cpp * @brief Implementation of the SyntheticGenerator class - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-02-14 + * @date 2024-02-04 **/ #include using namespace exageostat::generators::synthetic; using namespace exageostat::dataunits; -using namespace exageostat::hardware; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::kernels; using namespace exageostat::helpers; using namespace exageostat::linearAlgebra; @@ -34,15 +32,15 @@ SyntheticGenerator *SyntheticGenerator::GetInstance() { template std::unique_ptr> -SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, +SyntheticGenerator::CreateData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) { int n = aConfigurations.GetProblemSize() * aConfigurations.GetTimeSlot(); auto data = std::make_unique>(n, aConfigurations.GetDimension()); + // Allocated new Locations object. auto *locations = new Locations(n, aConfigurations.GetDimension()); - int parameters_number = aKernel.GetParametersNumbers(); // Set initial theta values. diff --git a/src/data-units/CMakeLists.txt b/src/data-units/CMakeLists.txt index d05e9096..ae2602f8 100644 --- a/src/data-units/CMakeLists.txt +++ b/src/data-units/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-02-27 diff --git a/src/data-units/DescriptorData.cpp b/src/data-units/DescriptorData.cpp index c74ee931..1e7857b1 100644 --- a/src/data-units/DescriptorData.cpp +++ b/src/data-units/DescriptorData.cpp @@ -6,7 +6,7 @@ /** * @file DescriptorData.cpp * @brief Contains the definition of the DescriptorData class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-18 diff --git a/src/data-units/ExaGeoStatData.cpp b/src/data-units/ExaGeoStatData.cpp index f64078e0..05185123 100644 --- a/src/data-units/ExaGeoStatData.cpp +++ b/src/data-units/ExaGeoStatData.cpp @@ -6,13 +6,14 @@ /** * @file ExaGeoStatData.cpp * @brief Contains the implementation of the ExaGeoStatData class. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-07-21 + * @date 2024-02-04 **/ #include +#include using namespace exageostat::dataunits; using namespace exageostat::common; @@ -23,6 +24,13 @@ ExaGeoStatData::ExaGeoStatData(const int &aSize, const Dimension &aDimension) this->mpDescriptorData = new DescriptorData(); } +template +ExaGeoStatData::ExaGeoStatData(const int &aSize, const std::string &aDimension) { + + this->mpLocations = new Locations(aSize, GetInputDimension(aDimension)); + this->mpDescriptorData = new DescriptorData(); +} + template ExaGeoStatData::~ExaGeoStatData() { delete this->mpLocations; @@ -70,7 +78,7 @@ void ExaGeoStatData::CalculateMedianLocations(const std::string &aKernelName, T x_min = this->mpLocations->GetLocationX()[0], x_max = this->mpLocations->GetLocationX()[0], y_min = this->mpLocations->GetLocationY()[0], y_max = this->mpLocations->GetLocationY()[0], z_min, z_max; - if (this->mpLocations->GetDimension() != common::Dimension2D) { + if (this->mpLocations->GetDimension() != Dimension2D) { z_min = this->mpLocations->GetLocationZ()[0]; z_max = this->mpLocations->GetLocationZ()[0]; } @@ -84,7 +92,7 @@ void ExaGeoStatData::CalculateMedianLocations(const std::string &aKernelName, y_min = (y < y_min) ? y : y_min; y_max = (y > y_max) ? y : y_max; - if (this->mpLocations->GetDimension() != common::Dimension2D) { + if (this->mpLocations->GetDimension() != Dimension2D) { T z = this->mpLocations->GetLocationX()[i]; z_min = (z < z_min) ? z : z_min; z_max = (z > z_max) ? z : z_max; @@ -93,13 +101,13 @@ void ExaGeoStatData::CalculateMedianLocations(const std::string &aKernelName, aLocations.GetLocationX()[0] = x_min + (x_max - x_min) / 2; aLocations.GetLocationY()[0] = y_min + (y_max - y_min) / 2; - if (this->mpLocations->GetDimension() != common::Dimension2D) { + if (this->mpLocations->GetDimension() != Dimension2D) { aLocations.GetLocationZ()[0] = z_min + (z_max - z_min) / 2; } } else { aLocations.GetLocationX()[0] = 0.5; aLocations.GetLocationY()[0] = 0.5; - if (this->mpLocations->GetDimension() != common::Dimension2D) { + if (this->mpLocations->GetDimension() != Dimension2D) { aLocations.GetLocationY()[0] = 0.5; } } diff --git a/src/data-units/Locations.cpp b/src/data-units/Locations.cpp index a2605ee3..3e511f8a 100644 --- a/src/data-units/Locations.cpp +++ b/src/data-units/Locations.cpp @@ -6,7 +6,7 @@ /** * @file Locations.cpp * @brief Implementation of the Locations class - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-27 diff --git a/src/data-units/descriptor/CMakeLists.txt b/src/data-units/descriptor/CMakeLists.txt index fd3cff70..316da3ef 100644 --- a/src/data-units/descriptor/CMakeLists.txt +++ b/src/data-units/descriptor/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-08-15 diff --git a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp index 19eeb6ab..c8ab42e2 100644 --- a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp +++ b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStatDescriptor.cpp * @brief Implementation of creating matrix descriptors used in CHAMELEON and HiCMA libraries. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-17 diff --git a/src/data-units/descriptor/concrete/CMakeLists.txt b/src/data-units/descriptor/concrete/CMakeLists.txt index af483ee9..ca3190e8 100644 --- a/src/data-units/descriptor/concrete/CMakeLists.txt +++ b/src/data-units/descriptor/concrete/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @brief CMake build script for the descriptors library, which includes the concrete implementations of the # Descriptors class based on the enabled libraries (HiCMA or Chameleon). # @author Mahmoud ElKarargy diff --git a/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp b/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp index bb62abc8..706e66d9 100644 --- a/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp +++ b/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file ChameleonDescriptor.cpp * @brief Defines the ChameleonDescriptor class for creating matrix descriptors using the CHAMELEON library. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp index 88650700..9ba22f79 100644 --- a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp +++ b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file HicmaDescriptor.cpp * @brief Defines the Hicma Descriptor class for creating matrix descriptors using the HICMA library. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/src/hardware/CMakeLists.txt b/src/hardware/CMakeLists.txt index 4d5f98f4..40242b3b 100644 --- a/src/hardware/CMakeLists.txt +++ b/src/hardware/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief This file contains the CMake configuration for the configurations directory. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-08-08 diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index cfe86381..f74d8e5c 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -6,10 +6,10 @@ /** * @file ExaGeoStatHardware.cpp * @brief Contains the implementation of the ExaGeoStatHardware class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-08-07 + * @date 2024-02-04 **/ #include @@ -17,15 +17,23 @@ #include #include #include -#include +#include +#include -using namespace exageostat::hardware; - -ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, const int &aCoreNumber, +ExaGeoStatHardware::ExaGeoStatHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber) { + this->InitHardware(aComputation, aCoreNumber, aGpuNumber); +} + +// Constructor for R +ExaGeoStatHardware::ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber) { + this->InitHardware(GetInputComputation(aComputation), aCoreNumber, aGpuNumber); +} + +void ExaGeoStatHardware::InitHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, + const int &aGpuNumber) { LOGGER("** Initialise ExaGeoStat hardware **") - this->mComputation = aComputation; int tag_width = 31, tag_sep = 26; // Init hardware using Chameleon if (!this->mpChameleonContext) { @@ -34,8 +42,8 @@ ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, this->mpChameleonContext = chameleon_context_self(); } - // Init hardware using Hicma - if (aComputation == common::TILE_LOW_RANK) { + // Init hardware using HiCMA + if (aComputation == exageostat::common::TILE_LOW_RANK) { #ifdef USE_HICMA if (!this->mpHicmaContext) { HICMA_user_tag_size(tag_width, tag_sep); @@ -46,11 +54,11 @@ ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, throw std::runtime_error("You need to enable Hicma to use TLR computation!"); #endif } - helpers::CommunicatorMPI::GetInstance()->SetHardwareInitialization(); + exageostat::helpers::CommunicatorMPI::GetInstance()->SetHardwareInitialization(); } ExaGeoStatHardware::~ExaGeoStatHardware() { - // finalize hardware using Hicma + // finalize hardware using HiCMA // finalize hardware using Chameleon if (!this->mpChameleonContext) { std::cerr << "No initialized context of Chameleon, Please initialize a hardware first" << std::endl; @@ -59,20 +67,15 @@ ExaGeoStatHardware::~ExaGeoStatHardware() { CHAMELEON_Finalize() this->mpChameleonContext = nullptr; } - if (this->mComputation == common::TILE_LOW_RANK) { #ifdef USE_HICMA - if (!this->mpHicmaContext) { - std::cout - << "No initialized context of HiCMA, Please use 'ExaGeoStatHardware::ExaGeoStatHardware(aComputation, CoreNumber, aGpuNumber);'" - << std::endl; - } else { - HICMA_Finalize(); - this->mpHicmaContext = nullptr; - } -#endif + // In case of HiCMA, It may be enabled but without initialize the hardware. aka: dense or dst computation. + if (this->mpHicmaContext) { + HICMA_Finalize(); + this->mpHicmaContext = nullptr; } - helpers::CommunicatorMPI::GetInstance()->RemoveHardwareInitialization(); - results::Results::GetInstance()->PrintEndSummary(); +#endif + exageostat::helpers::CommunicatorMPI::GetInstance()->RemoveHardwareInitialization(); + exageostat::results::Results::GetInstance()->PrintEndSummary(); } #ifdef USE_HICMA @@ -93,11 +96,11 @@ void *ExaGeoStatHardware::GetChameleonContext() const { return this->mpChameleonContext; } -void *ExaGeoStatHardware::GetContext(common::Computation aComputation) const { - if (aComputation == common::EXACT_DENSE || aComputation == common::DIAGONAL_APPROX) { +void *ExaGeoStatHardware::GetContext(exageostat::common::Computation aComputation) const { + if (aComputation == exageostat::common::EXACT_DENSE || aComputation == exageostat::common::DIAGONAL_APPROX) { return GetChameleonContext(); } - if (aComputation == common::TILE_LOW_RANK) { + if (aComputation == exageostat::common::TILE_LOW_RANK) { #ifdef USE_HICMA return GetHicmaContext(); #endif @@ -105,6 +108,3 @@ void *ExaGeoStatHardware::GetContext(common::Computation aComputation) const { return nullptr; } -ExaGeoStatHardware::ExaGeoStatHardware(const int &aCoreNumber, const int &aGpuNumber) { - -} diff --git a/src/helpers/CMakeLists.txt b/src/helpers/CMakeLists.txt index 5c01b62a..cbcb2d5c 100644 --- a/src/helpers/CMakeLists.txt +++ b/src/helpers/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-06-08 diff --git a/src/helpers/CommunicatorMPI.cpp b/src/helpers/CommunicatorMPI.cpp index e9167c4b..9f8ac581 100644 --- a/src/helpers/CommunicatorMPI.cpp +++ b/src/helpers/CommunicatorMPI.cpp @@ -6,7 +6,7 @@ /** * @file CommunicatorMPI.cpp * @brief Defines the CommunicatorMPI class for MPI rank communication. - * @version 1.0.1 + * @version 1.1.0 * @author Sameh Abdulah * @date 2023-11-10 **/ diff --git a/src/helpers/DiskWriter.cpp b/src/helpers/DiskWriter.cpp index b168c634..3f83bc06 100644 --- a/src/helpers/DiskWriter.cpp +++ b/src/helpers/DiskWriter.cpp @@ -6,7 +6,7 @@ /** * @file DiskWriter.cpp * @brief Contains the implementation of the DiskWriter class for writing data to disk. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/src/helpers/DistanceCalculationHelpers.cpp b/src/helpers/DistanceCalculationHelpers.cpp index 3ee132cb..33aac868 100644 --- a/src/helpers/DistanceCalculationHelpers.cpp +++ b/src/helpers/DistanceCalculationHelpers.cpp @@ -6,7 +6,7 @@ /** * @file DistanceCalculationHelpers.cpp * @brief Contains the implementation of the DistanceCalculationHelpers class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/src/kernels/CMakeLists.txt b/src/kernels/CMakeLists.txt index 7a8a55da..ce472997 100644 --- a/src/kernels/CMakeLists.txt +++ b/src/kernels/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief This file contains the CMake configuration for the kernels directory. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-04-11 diff --git a/src/kernels/Kernel.cpp b/src/kernels/Kernel.cpp index 953cbb7c..08ee91b8 100644 --- a/src/kernels/Kernel.cpp +++ b/src/kernels/Kernel.cpp @@ -6,7 +6,7 @@ /** * @file Kernel.cpp * @brief implementation file for the Kernels class, which contains the main kernel functions. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-12 diff --git a/src/kernels/concrete/BivariateMaternFlexible.cpp b/src/kernels/concrete/BivariateMaternFlexible.cpp index 39a67c18..945da9f9 100644 --- a/src/kernels/concrete/BivariateMaternFlexible.cpp +++ b/src/kernels/concrete/BivariateMaternFlexible.cpp @@ -6,7 +6,7 @@ /** * @file BivariateMaternFlexible.cpp * @brief Implementation of the BivariateMaternFlexible kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/BivariateMaternParsimonious.cpp b/src/kernels/concrete/BivariateMaternParsimonious.cpp index 223dbd3e..284a0e66 100644 --- a/src/kernels/concrete/BivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/BivariateMaternParsimonious.cpp @@ -6,7 +6,7 @@ /** * @file BivariateMaternParsimonious.cpp * @brief Implementation of the BivariateMaternParsimonious kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp index 08e65389..8edc047c 100644 --- a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp @@ -6,7 +6,7 @@ /** * @file BivariateSpacetimeMaternStationary.cpp * @brief Implementation of the BivariateSpacetimeMaternStationary kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/TrivariateMaternParsimonious.cpp b/src/kernels/concrete/TrivariateMaternParsimonious.cpp index d5973678..8050433e 100644 --- a/src/kernels/concrete/TrivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/TrivariateMaternParsimonious.cpp @@ -6,7 +6,7 @@ /** * @file TrivariateMaternParsimonious.cpp * @brief Implementation of the BivariateMaternParsimonious kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateExpNonGaussian.cpp b/src/kernels/concrete/UnivariateExpNonGaussian.cpp index 2007d78d..e4a31d1b 100644 --- a/src/kernels/concrete/UnivariateExpNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateExpNonGaussian.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateExpNonGaussian.cpp * @brief Implementation of the UnivariateExpNonGaussian kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDbeta.cpp b/src/kernels/concrete/UnivariateMaternDbeta.cpp index c0253e94..8f2cbda5 100644 --- a/src/kernels/concrete/UnivariateMaternDbeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDbeta.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDbeta.cpp * @brief Implementation of the UnivariateMaternDbeta kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp index dec11267..6de1d251 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdbetaBeta.cpp * @brief Implementation of the UnivariateMaternDdbetaBeta kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp index e202b96d..e590e59e 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdbetaNu.cpp * @brief Implementation of the UnivariateMaternDdbetaNu kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp index 7dec948b..c2352493 100644 --- a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdnuNu.cpp * @brief Implementation of the UnivariateMaternDdnuNu kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp index ee980159..6d4af4b4 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquare.cpp * @brief Implementation of the UnivariateMaternDdsigmaSquare kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp index 79c15bc4..4baf4b32 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquareBeta.cpp * @brief Implementation of the UnivariateMaternDdsigmaSquareBeta kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp index 19623e65..5b04e2cb 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquareNu.cpp * @brief Implementation of the UnivariateMaternDdsigmaSquareNu kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDnu.cpp b/src/kernels/concrete/UnivariateMaternDnu.cpp index c62e47c1..9a012044 100644 --- a/src/kernels/concrete/UnivariateMaternDnu.cpp +++ b/src/kernels/concrete/UnivariateMaternDnu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDnu.cpp * @brief Implementation of the UnivariateMaternDnu kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp index 2973c2af..7ce72c82 100644 --- a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDsigmaSquare.cpp * @brief Implementation of the UnivariateMaternDsigmaSquare kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp index 6d871568..76f5ae3b 100644 --- a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternNonGaussian.cpp * @brief Implementation of the UnivariateMaternNonGaussian kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp index 7d899d7b..d29ac1f2 100644 --- a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternNuggetsStationary.cpp * @brief Implementation of the UnivariateMaternNuggetsStationary kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternStationary.cpp b/src/kernels/concrete/UnivariateMaternStationary.cpp index 93de1667..f878bf1e 100644 --- a/src/kernels/concrete/UnivariateMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternStationary.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternStationary.cpp * @brief Implementation of the UnivariateMaternStationary kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp index 19a99bce..a789a56a 100644 --- a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateSpacetimeMaternStationary.cpp * @brief Implementation of the UnivariateSpacetimeMaternStationary kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/linear-algebra-solvers/CMakeLists.txt b/src/linear-algebra-solvers/CMakeLists.txt index d3aea1ca..0eb0f8a9 100644 --- a/src/linear-algebra-solvers/CMakeLists.txt +++ b/src/linear-algebra-solvers/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @brief CMake build script for the linear-algebra-solvers library, which includes the LinearAlgebraMethods base class and the LinearAlgebraFactory class for creating linear algebra solvers for different computations using HiCMA or Chameleon libraries. # @author Mahmoud ElKarargy # @author Sameh Abdulah diff --git a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp index 0707343f..040d2e11 100644 --- a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp @@ -27,7 +27,6 @@ using namespace exageostat::linearAlgebra; using namespace exageostat::common; -using namespace exageostat::configurations; template std::unique_ptr> LinearAlgebraFactory::CreateLinearAlgebraSolver(Computation aComputation) { diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index 76af27ff..b375c547 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -6,10 +6,10 @@ /** * @file LinearAlgebraMethods.cpp * @brief Implementation of linear algebra methods. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-03-20 + * @date 2024-02-04 **/ #ifdef USE_MPI @@ -25,8 +25,6 @@ using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; using namespace exageostat::dataunits; -using namespace exageostat::configurations; -using namespace exageostat::hardware; // Define a method to set up the Chameleon descriptors template @@ -114,7 +112,7 @@ void LinearAlgebraMethods::InitiateDescriptors(Configurations &aConfiguration } template -void LinearAlgebraMethods::InitiateFisherDescriptors(configurations::Configurations &aConfigurations, +void LinearAlgebraMethods::InitiateFisherDescriptors(Configurations &aConfigurations, dataunits::DescriptorData &aDescriptorData) { // Check for initialize the Chameleon context. @@ -181,7 +179,7 @@ void LinearAlgebraMethods::InitiateFisherDescriptors(configurations::Configur template void LinearAlgebraMethods::InitiatePredictionDescriptors( - Configurations &aConfigurations, std::unique_ptr> &aData, const int &aP) { + Configurations &aConfigurations, std::unique_ptr> &aData, const int &aP) { if (!this->mpContext) { throw std::runtime_error( @@ -253,7 +251,7 @@ void LinearAlgebraMethods::InitiatePredictionDescriptors( template void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, const int &aP) { if (!this->mpContext) { @@ -316,9 +314,9 @@ void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfi } template -void LinearAlgebraMethods::GenerateSyntheticData(configurations::Configurations &aConfigurations, - const hardware::ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, +void LinearAlgebraMethods::GenerateSyntheticData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, const kernels::Kernel &aKernel) { this->mpContext = aHardware.GetChameleonContext(); @@ -330,7 +328,7 @@ void LinearAlgebraMethods::GenerateSyntheticData(configurations::Configuratio template void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, Locations *apLocation1, Locations *apLocation2, Locations *apLocation3, const int &aDistanceMetric, const kernels::Kernel &aKernel) { @@ -446,7 +444,7 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig } template -T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, +T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, const ExaGeoStatHardware &aHardware, Configurations &aConfiguration, Locations &aMissLocations, @@ -597,12 +595,12 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptr -T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, +T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfiguration, + const ExaGeoStatHardware &aHardware, + Configurations &aConfiguration, dataunits::Locations &aMissLocations, dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel) { @@ -800,7 +798,7 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< template void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, const ExaGeoStatHardware &aHardware, T *apTruthTheta, T *apEstimatedTheta, Locations &aMissLocations, Locations &aObsLocations, @@ -1133,9 +1131,9 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu } template -T *LinearAlgebraMethods::ExaGeoStatFisherTile(configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, - const hardware::ExaGeoStatHardware &aHardware, T *apTheta, +T *LinearAlgebraMethods::ExaGeoStatFisherTile(Configurations &aConfigurations, + std::unique_ptr> &aData, + const ExaGeoStatHardware &aHardware, T *apTheta, const kernels::Kernel &aKernel) { this->SetContext(aHardware.GetChameleonContext()); diff --git a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp index 34e7af92..b3c70554 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp @@ -7,10 +7,10 @@ * @file ChameleonImplementation.cpp * @brief This file contains the declaration of ChameleonImplementation class. * @details ChameleonImplementation is a concrete implementation of LinearAlgebraMethods class for dense or diagonal-super tile matrices.. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-03-20 + * @date 2024-02-04 **/ #ifdef USE_MPI @@ -25,7 +25,6 @@ using namespace std; using namespace exageostat::common; using namespace exageostat::dataunits; -using namespace exageostat::configurations; using namespace exageostat::linearAlgebra; template @@ -144,7 +143,7 @@ ChameleonImplementation::ExaGeoStatNonGaussianTransformTileAsync(void *apDesc } template -T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, +T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) { diff --git a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp index 699e8e04..8b087393 100644 --- a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp @@ -9,7 +9,7 @@ * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-03-26 + * @date 2024-02-04 **/ #include @@ -21,15 +21,13 @@ using namespace exageostat::common; using namespace exageostat::dataunits; using namespace exageostat::kernels; using namespace exageostat::helpers; -using namespace exageostat::hardware; -using namespace exageostat::configurations; int store_only_diagonal_tiles = 1; int use_scratch = 1; int global_check = 0; //used to create dense matrix for accuracy check template -void HicmaImplementation::SetModelingDescriptors(std::unique_ptr> &aData, +void HicmaImplementation::SetModelingDescriptors(std::unique_ptr> &aData, Configurations &aConfigurations, const int &aP) { int full_problem_size = aConfigurations.GetProblemSize() * aP; @@ -96,8 +94,8 @@ void HicmaImplementation::SetModelingDescriptors(std::unique_ptr -T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, +T HicmaImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const Kernel &aKernel) { diff --git a/src/prediction/CMakeLists.txt b/src/prediction/CMakeLists.txt index 63a1f946..d43bde5c 100644 --- a/src/prediction/CMakeLists.txt +++ b/src/prediction/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-06-08 diff --git a/src/prediction/Prediction.cpp b/src/prediction/Prediction.cpp index dbb9817d..b42e98b1 100644 --- a/src/prediction/Prediction.cpp +++ b/src/prediction/Prediction.cpp @@ -6,10 +6,10 @@ /** * @file Prediction.cpp * @brief Contains the implementation of the Prediction class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-06-08 + * @date 2024-02-04 **/ #include @@ -18,11 +18,10 @@ #include using namespace exageostat::prediction; -using namespace exageostat::configurations; using namespace exageostat::dataunits; template -void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHardware, +void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, Configurations &aConfigurations, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) { diff --git a/src/prediction/PredictionAuxiliaryFunctions.cpp b/src/prediction/PredictionAuxiliaryFunctions.cpp index 1dd7b33b..52fecd42 100644 --- a/src/prediction/PredictionAuxiliaryFunctions.cpp +++ b/src/prediction/PredictionAuxiliaryFunctions.cpp @@ -6,7 +6,7 @@ /** * @file PredictionAuxiliaryFunctions.cpp * @brief Contains the implementation of the PredictionAuxiliaryFunctions class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 @@ -16,7 +16,7 @@ #include #include -#include +#include using namespace exageostat::prediction; using namespace exageostat::dataunits; diff --git a/src/prediction/PredictionHelpers.cpp b/src/prediction/PredictionHelpers.cpp index 90da508b..d0f0f88e 100644 --- a/src/prediction/PredictionHelpers.cpp +++ b/src/prediction/PredictionHelpers.cpp @@ -6,7 +6,7 @@ /** * @file PredictionHelpers.cpp * @brief Contains the implementation of the PredictionHelpers class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 @@ -17,12 +17,11 @@ #include using namespace exageostat::prediction; -using namespace exageostat::configurations; using namespace exageostat::dataunits; template void PredictionHelpers::PickRandomPoints(Configurations &aConfigurations, - std::unique_ptr> &aData, T *apZObs, + std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, Locations &aMissLocation, Locations &aObsLocation, const int &aP) { diff --git a/src/results/CMakeLists.txt b/src/results/CMakeLists.txt index 8b7adbbc..72002da3 100644 --- a/src/results/CMakeLists.txt +++ b/src/results/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-09-14 diff --git a/src/results/Results.cpp b/src/results/Results.cpp index 46805935..5f831dc6 100644 --- a/src/results/Results.cpp +++ b/src/results/Results.cpp @@ -6,13 +6,13 @@ /** * @file Results.cpp * @brief Defines the Results class for storing and accessing result data. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-09-14 + * @date 2024-02-04 **/ #include -#include +#include using namespace exageostat::results; using namespace exageostat::common; @@ -43,8 +43,8 @@ void Results::SetLoggerPath(const std::string &aLoggerPath) { void Results::PrintEndSummary() { - Verbose temp = exageostat::configurations::Configurations::GetVerbosity(); - exageostat::configurations::Configurations::SetVerbosity(STANDARD_MODE); + Verbose temp = Configurations::GetVerbosity(); + Configurations::SetVerbosity(STANDARD_MODE); LOGGER("") LOGGER("********************SUMMARY**********************") @@ -125,7 +125,7 @@ void Results::PrintEndSummary() { LOGGER("") } LOGGER("*************************************************") - exageostat::configurations::Configurations::SetVerbosity(temp); + Configurations::SetVerbosity(temp); } void Results::SetMLEIterations(int aIterationsNumber) { diff --git a/tests/cpp-tests/api/CMakeLists.txt b/tests/cpp-tests/api/CMakeLists.txt index 9748ef3d..4d46e039 100644 --- a/tests/cpp-tests/api/CMakeLists.txt +++ b/tests/cpp-tests/api/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-08-08 diff --git a/tests/cpp-tests/api/TestExaGeoStatApi.cpp b/tests/cpp-tests/api/TestExaGeoStatApi.cpp index 4074803f..a5413e8f 100644 --- a/tests/cpp-tests/api/TestExaGeoStatApi.cpp +++ b/tests/cpp-tests/api/TestExaGeoStatApi.cpp @@ -6,10 +6,10 @@ /** * @file TestExaGeoStatApi.cpp * @brief Test suite for the ExaGeoStat APIs data generation functionality. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-08-07 + * @date 2024-02-04 **/ #include @@ -18,8 +18,6 @@ using namespace std; using namespace exageostat::common; -using namespace exageostat::configurations; -using namespace exageostat::hardware; void TEST_GENERATE_DATA() { SECTION("Data generation - Observations") @@ -47,7 +45,7 @@ void TEST_GENERATE_DATA() { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(EXACT_DENSE, 4, 0); // Or you could use configurations.GetComputation(). - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); @@ -108,7 +106,7 @@ void TEST_MODEL_DATA(Computation aComputation) { { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(aComputation, 4, 0); // Or you could use configurations.GetComputation(). - std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); //initiating the matrix of the CHAMELEON Descriptor Z. @@ -147,7 +145,7 @@ void TEST_MODEL_DATA(Computation aComputation) { { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(aComputation, 4, 0); // Or you could use configurations.GetComputation(). - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); double log_likelihood = exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, @@ -181,7 +179,7 @@ void TEST_PREDICTION() { Configurations::SetVerbosity(QUIET_MODE); auto hardware = ExaGeoStatHardware(EXACT_DENSE, configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); - std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); auto *z_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, diff --git a/tests/cpp-tests/configurations/TestConfigurations.cpp b/tests/cpp-tests/configurations/TestConfigurations.cpp index 12813246..fe8873db 100644 --- a/tests/cpp-tests/configurations/TestConfigurations.cpp +++ b/tests/cpp-tests/configurations/TestConfigurations.cpp @@ -18,12 +18,11 @@ #include #include -#include "configurations/Configurations.hpp" +#include using namespace std; using namespace exageostat::common; -using namespace exageostat::configurations; void TEST_SYNTHETIC_CONFIGURATIONS() { diff --git a/tests/cpp-tests/data-generators/CMakeLists.txt b/tests/cpp-tests/data-generators/CMakeLists.txt index c3e1f4de..df341db5 100644 --- a/tests/cpp-tests/data-generators/CMakeLists.txt +++ b/tests/cpp-tests/data-generators/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-03-08 diff --git a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp index 83667823..1ada7d9c 100644 --- a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp @@ -9,7 +9,7 @@ * @details This file contains Catch2 unit tests that validate the functionality of the CSVDataGenerator class * in the ExaGeoStat software package. The tests cover various aspects of data generation, including spreading * and reversing bits, generating locations for different dimensions, and testing helper functions. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-03-08 **/ @@ -25,7 +25,6 @@ using namespace std; using namespace exageostat::generators; using namespace exageostat::dataunits; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::kernels; void TEST_CSV_P_1() { @@ -36,7 +35,6 @@ void TEST_CSV_P_1() { read_path = read_path + +"tests/cpp-tests/data-generators/concrete/synthetic_ds/SYN_16_1"; Configurations configurations; - configurations.SetIsCSV(true); configurations.SetIsSynthetic(false); configurations.SetProblemSize(16); configurations.SetDenseTileSize(8); @@ -49,7 +47,7 @@ void TEST_CSV_P_1() { Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); - auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); @@ -190,7 +188,6 @@ void TEST_CSV_P_2() { read_path = read_path + +"tests/cpp-tests/data-generators/concrete/synthetic_ds/SYN_16_1"; Configurations configurations; - configurations.SetIsCSV(true); configurations.SetIsSynthetic(false); configurations.SetProblemSize(16); configurations.SetDenseTileSize(8); @@ -201,7 +198,7 @@ void TEST_CSV_P_2() { Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); int p = pKernel->GetVariablesNumber(); - auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); @@ -362,7 +359,6 @@ void TEST_CSV_P_3() { read_path = read_path + +"tests/cpp-tests/data-generators/concrete/synthetic_ds/SYN_16_1"; Configurations configurations; - configurations.SetIsCSV(true); configurations.SetIsSynthetic(false); configurations.SetProblemSize(16); configurations.SetDenseTileSize(3); @@ -375,7 +371,7 @@ void TEST_CSV_P_3() { Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); int p = pKernel->GetVariablesNumber(); - auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); diff --git a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp index 3ff0971f..207e5b1c 100644 --- a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp @@ -9,7 +9,7 @@ * @details This file contains Catch2 unit tests that validate the functionality of the SyntheticGenerator class * in the ExaGeoStat software package. The tests cover various aspects of data generation, including spreading * and reversing bits, generating locations for different dimensions, and testing helper functions. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-03-08 **/ @@ -27,7 +27,6 @@ using namespace exageostat::generators::synthetic; using namespace exageostat::generators; using namespace exageostat::dataunits; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::kernels; void TEST_SPREAD_REVERSED_BITS() { @@ -194,7 +193,7 @@ void TEST_GENERATE_LOCATIONS() { Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(synthetic_data_configurations.GetKernelName(), synthetic_data_configurations.GetTimeSlot()); - auto hardware = exageostat::hardware::ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), + auto hardware = ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), synthetic_data_configurations.GetCoresNumber(), synthetic_data_configurations.GetGPUsNumbers()); SECTION("2D Generation") @@ -297,7 +296,7 @@ void TEST_GENERATION() { synthetic_data_configurations.SetDenseTileSize(1); synthetic_data_configurations.SetKernelName("UnivariateMaternStationary"); synthetic_data_configurations.SetComputation(exageostat::common::EXACT_DENSE); - auto hardware = exageostat::hardware::ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), + auto hardware = ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), synthetic_data_configurations.GetCoresNumber(), synthetic_data_configurations.GetGPUsNumbers()); diff --git a/tests/cpp-tests/helpers/CMakeLists.txt b/tests/cpp-tests/helpers/CMakeLists.txt index 4bae3c7a..7e71df57 100644 --- a/tests/cpp-tests/helpers/CMakeLists.txt +++ b/tests/cpp-tests/helpers/CMakeLists.txt @@ -3,7 +3,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-12-08 diff --git a/tests/cpp-tests/helpers/TestPredictionHelpers.cpp b/tests/cpp-tests/helpers/TestPredictionHelpers.cpp index b69b45fe..cefd8e79 100644 --- a/tests/cpp-tests/helpers/TestPredictionHelpers.cpp +++ b/tests/cpp-tests/helpers/TestPredictionHelpers.cpp @@ -7,7 +7,7 @@ * @file TestPrediction.cpp * @brief Unit tests for the TestPrediction class in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestPrediction class - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-12-08 **/ diff --git a/tests/cpp-tests/kernels/CMakeLists.txt b/tests/cpp-tests/kernels/CMakeLists.txt index 01d039c3..1c34c6d5 100644 --- a/tests/cpp-tests/kernels/CMakeLists.txt +++ b/tests/cpp-tests/kernels/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-04-29 diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp index 48cee8bb..74bf95a8 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp @@ -7,7 +7,7 @@ * @brief Unit tests for the BivariateMaternFlexible kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the BivariateMaternFlexible kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-09 @@ -19,10 +19,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_BivariateMaternFlexible() { @@ -49,7 +47,7 @@ void TEST_KERNEL_GENERATION_BivariateMaternFlexible() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp index cda682fe..22378cb7 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestBivariateMaternParsimonious kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestBivariateMaternParsimonious kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_BivariateMaternParsimonious() { @@ -50,7 +48,7 @@ void TEST_KERNEL_GENERATION_BivariateMaternParsimonious() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp index 6b0d4387..c673306e 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestBivariateSpacetimeMaternStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestBivariateSpacetimeMaternStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_BivariateSpacetimeMaternStationary() { @@ -51,7 +49,7 @@ void TEST_KERNEL_GENERATION_BivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp index 4859dceb..e6d0057e 100644 --- a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestTrivariateMaternParsimonious kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestTrivariateMaternParsimonious kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_TrivariateMaternParsimonious() { @@ -48,7 +46,7 @@ void TEST_KERNEL_GENERATION_TrivariateMaternParsimonious() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp index 18abd91e..c1697056 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp @@ -6,7 +6,7 @@ /** * @file TestUnivariateExpNonGaussian.cpp * @brief - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp index 0ec31efd..1ed3d570 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDbeta kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDbeta kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -18,7 +18,7 @@ #include using namespace std; -using namespace exageostat::configurations; + using namespace exageostat::common; void TEST_KERNEL_GENERATION_UnivariateMaternDbeta() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp index 9102f9b6..d4cd5ecc 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdbetaBeta kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdbetaBeta kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,7 +19,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp index 903ef13a..d20272c2 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdbetaNu kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdbetaNu kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,10 +19,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariateMaternDdbetaNu() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp index 4e0b3a74..12ecc421 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdnuNu kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdnuNu kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,7 +19,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp index f3e9d072..a84bbdce 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdsigmaSquare kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdsigmaSquare kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -17,12 +17,11 @@ #include #include -using namespace exageostat::configurations; +using namespace std; + using namespace exageostat::common; using namespace exageostat::api; -using namespace std; - void TEST_KERNEL_GENERATION_UnivariateMaternDdsigmaSquare() { SECTION("UnivariateMaternDdsigmaSquare") diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp index 9f277794..053a222b 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdsigmaSquareBetaBeta kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdsigmaSquareBetaBeta kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -18,7 +18,7 @@ #include using namespace std; -using namespace exageostat::configurations; + using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp index 566786bc..22051826 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdsigmaSquareNu kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdsigmaSquareNu kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,7 +19,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp index 5d212512..7577b33d 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDnu kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDnu kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,7 +19,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp index dde48ff4..c38c9ac7 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdsigmaSquare kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdsigmaSquare kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-29 @@ -19,7 +19,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp index 31fbd107..27e9ce93 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternNonGaussian kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternNonGaussian kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,10 +19,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { @@ -52,7 +50,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp index 1f43a7fd..0142e74c 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternNuggetsStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternNuggetsStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariateMaternNuggetsStationary() { @@ -48,7 +46,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNuggetsStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp index a9b4777a..7f00ccd5 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-29 @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariateMaternStationary() { @@ -49,7 +47,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp index 65147642..526022ba 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateSpacetimeMaternStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateSpacetimeMaternStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,10 +19,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariateSpacetimeMaternStationary() { @@ -50,7 +48,7 @@ void TEST_KERNEL_GENERATION_UnivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt b/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt index f7afad0d..93440ee8 100644 --- a/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt +++ b/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-04-06 diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp index 8c956b1b..0b97a447 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp @@ -6,7 +6,7 @@ /** * @file TestChameleonImplementationDST.cpp * @brief Unit tests for the Diagonal Super Tile computation in the ExaGeoStat software package. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-04-09 **/ @@ -25,9 +25,7 @@ using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::dataunits; -using namespace exageostat::hardware; //Test that the function initializes the CHAM_descriptorC descriptor correctly. diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp index a7d2c204..61839081 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp @@ -6,7 +6,7 @@ /** * @file TestChameleonImplmentationDense.cpp * @brief Unit tests for the Dense computation in the ExaGeoStat software package. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-04-06 **/ @@ -25,9 +25,7 @@ using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::dataunits; -using namespace exageostat::hardware; //Test that the function initializes the CHAM_descriptorC descriptor correctly. void TEST_CHAMELEON_DESCRIPTORS_VALUES() { diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp index 9d0c7877..62a5ce94 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp @@ -6,7 +6,7 @@ /** * @file TestHiCMAImplementationTLR.cpp * @brief Unit tests for the Tile Low Rank computation in the ExaGeoStat software package. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-04-09 **/ @@ -18,9 +18,7 @@ using namespace std; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::dataunits; -using namespace exageostat::hardware; //Test that the function initializes the HICMA_descriptorC descriptor correctly. void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { @@ -52,7 +50,7 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { auto hardware = ExaGeoStatHardware(TILE_LOW_RANK, 1, 0); synthetic_data_configurations.SetApproximationMode(1); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, synthetic_data_configurations, data); @@ -86,7 +84,7 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { // initialize Hardware. auto hardware = ExaGeoStatHardware(TILE_LOW_RANK, 1, 0); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, synthetic_data_configurations, data); diff --git a/tests/heavy-tests/CMakeLists.txt b/tests/heavy-tests/CMakeLists.txt index b3feac02..f827077c 100644 --- a/tests/heavy-tests/CMakeLists.txt +++ b/tests/heavy-tests/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-12-20 diff --git a/tests/heavy-tests/ExamplesTests.cpp b/tests/heavy-tests/ExamplesTests.cpp index df7133f1..4b1906e9 100644 --- a/tests/heavy-tests/ExamplesTests.cpp +++ b/tests/heavy-tests/ExamplesTests.cpp @@ -6,7 +6,7 @@ /** * @file TestExaGeoStatApi.cpp * @brief Test suite for the ExaGeoStat APIs data generation functionality. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-12-26 **/ @@ -19,7 +19,6 @@ using namespace std; using namespace std::filesystem; -using namespace exageostat::configurations; using namespace exageostat::dataunits; using namespace exageostat::api; diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index 1bf702f0..1d71687c 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -6,7 +6,7 @@ /** * @file TestExaGeoStatApi.cpp * @brief Test suite for the ExaGeoStat APIs data generation functionality. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-12-20 **/ @@ -22,7 +22,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::dataunits; using namespace exageostat::api; using namespace exageostat::kernels; From 2ea149e3730d4109c8eceea9bca07155fc9a6a7c Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 4 Feb 2024 19:10:22 +0200 Subject: [PATCH 19/82] Fix Jenkines --- Jenkinsfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 353913a8..a4aa6505 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,7 +27,7 @@ pipeline { module load mkl/2020.0.166 #################################################### set -x - ./config.sh -t -e + ./configure -t -e ./clean_build.sh ''' } @@ -69,7 +69,7 @@ pipeline { module load mkl/2020.0.166 #################################################### set -x - ./config.sh -t -e -H + ./configure -t -e -H ./clean_build.sh ''' } @@ -107,7 +107,7 @@ pipeline { # BLAS/LAPACK #################################################### module load mkl/2020.0.166 - ./config.sh -e + ./configure -e ./clean_build.sh cd bin make docs @@ -134,4 +134,4 @@ pipeline { emailext body: "${env.JOB_NAME} - Please go to ${env.BUILD_URL}", subject: "Jenkins Pipeline build FAILED", recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']] } } -} \ No newline at end of file +} From 64813243c0e2d9e4d9f0fe7bf8e7e64891aca38e Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 4 Feb 2024 19:28:05 +0200 Subject: [PATCH 20/82] fix errors --- Jenkinsfile | 2 +- inst/include/configurations/Configurations.hpp | 2 +- src/configurations/Configurations.cpp | 8 -------- tests/cpp-tests/api/TestExaGeoStatApi.cpp | 1 + .../kernels/concrete/TestBivariateMaternFlexible.cpp | 2 +- .../kernels/concrete/TestBivariateMaternParsimonious.cpp | 5 ++--- .../concrete/TestBivariateSpacetimeMaternStationary.cpp | 2 +- .../kernels/concrete/TestTrivariateMaternParsimonious.cpp | 2 +- .../kernels/concrete/TestUnivariateMaternNonGaussian.cpp | 2 +- .../concrete/TestUnivariateMaternNuggetsStationary.cpp | 2 +- .../kernels/concrete/TestUnivariateMaternStationary.cpp | 2 +- .../concrete/TestUnivariateSpacetimeMaternStationary.cpp | 2 +- 12 files changed, 12 insertions(+), 20 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a4aa6505..274dd7d3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -134,4 +134,4 @@ pipeline { emailext body: "${env.JOB_NAME} - Please go to ${env.BUILD_URL}", subject: "Jenkins Pipeline build FAILED", recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']] } } -} +} \ No newline at end of file diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index 4c3a9967..733b119f 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -77,7 +77,7 @@ class Configurations { * @brief Virtual destructor to allow calls to the correct concrete destructor. * */ - ~Configurations(); + ~Configurations() = default; /** * @brief Initialize the module arguments. diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index d71658c4..6ab6d4b8 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -66,14 +66,6 @@ Configurations::Configurations() { SetIsNonGaussian(false); } -Configurations::~Configurations() { - - for (int i = 0; i < this->mArgC; ++i) { - delete[] this->mpArgV[i]; - } - delete[] this->mpArgV; -} - void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { this->mArgC = aArgC; diff --git a/tests/cpp-tests/api/TestExaGeoStatApi.cpp b/tests/cpp-tests/api/TestExaGeoStatApi.cpp index a5413e8f..a2d84b9c 100644 --- a/tests/cpp-tests/api/TestExaGeoStatApi.cpp +++ b/tests/cpp-tests/api/TestExaGeoStatApi.cpp @@ -18,6 +18,7 @@ using namespace std; using namespace exageostat::common; +using namespace exageostat::dataunits; void TEST_GENERATE_DATA() { SECTION("Data generation - Observations") diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp index 74bf95a8..cf9ff7e5 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp @@ -47,7 +47,7 @@ void TEST_KERNEL_GENERATION_BivariateMaternFlexible() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp index 22378cb7..bbfcd5f5 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp @@ -48,9 +48,8 @@ void TEST_KERNEL_GENERATION_BivariateMaternParsimonious() { int seed = 0; srand(seed); - std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + std::unique_ptr> data; + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations,data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp index c673306e..4258194f 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp @@ -49,7 +49,7 @@ void TEST_KERNEL_GENERATION_BivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp index e6d0057e..531cb60b 100644 --- a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp @@ -46,7 +46,7 @@ void TEST_KERNEL_GENERATION_TrivariateMaternParsimonious() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp index 27e9ce93..e7d97c30 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp @@ -50,7 +50,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp index 0142e74c..380b4f93 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp @@ -46,7 +46,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNuggetsStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp index 7f00ccd5..e7830f8f 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp @@ -47,7 +47,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp index 526022ba..58144e6d 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp @@ -48,7 +48,7 @@ void TEST_KERNEL_GENERATION_UnivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, From 038e9230f737b8c56cbe650ea23234324b0b5903 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 4 Feb 2024 21:10:22 +0200 Subject: [PATCH 21/82] Updated in design --- .gitignore | 1 + CHANGELOG.md | 6 +- config.sh | 20 ++- examples/configurations/CMakeLists.txt | 6 +- .../RunningWithDifferentConfigurations.cpp | 75 +++++++++ ...tionModule.cpp => SetupConfigurations.cpp} | 12 +- inst/include/api/ExaGeoStat.hpp | 2 +- inst/include/common/Definitions.hpp | 9 ++ .../include/configurations/Configurations.hpp | 4 +- .../include/data-generators/DataGenerator.hpp | 8 +- .../data-generators/LocationGenerator.hpp | 73 +++++++++ .../concrete/CSVDataGenerator.hpp | 97 ------------ .../concrete/SyntheticGenerator.hpp | 58 ------- inst/include/data-loader/DataLoader.hpp | 67 ++++++++ .../data-loader/concrete/CSVLoader.hpp | 84 ++++++++++ inst/include/data-units/DescriptorData.hpp | 2 +- inst/include/data-units/Locations.hpp | 4 +- inst/include/hardware/ExaGeoStatHardware.hpp | 2 +- inst/include/helpers/BasselFunction.hpp | 69 ++++++++ inst/include/helpers/ByteHandler.hpp | 48 ++++++ inst/include/helpers/DiskWriter.hpp | 4 +- inst/include/kernels/Kernel.hpp | 28 +--- inst/include/prediction/Prediction.hpp | 31 ++-- .../PredictionAuxiliaryFunctions.hpp | 10 -- src/CMakeLists.txt | 1 + src/api/ExaGeoStat.cpp | 3 +- src/configurations/Configurations.cpp | 1 + src/data-generators/CMakeLists.txt | 3 +- src/data-generators/DataGenerator.cpp | 36 +++-- src/data-generators/LocationGenerator.cpp | 122 ++++++++++++++ src/data-generators/concrete/CMakeLists.txt | 2 - .../concrete/SyntheticGenerator.cpp | 149 +----------------- src/data-loader/CMakeLists.txt | 20 +++ src/data-loader/DataLoader.cpp | 70 ++++++++ src/data-loader/concrete/CMakeLists.txt | 17 ++ .../concrete/CSVLoader.cpp} | 72 ++------- src/helpers/BasselFunction.cpp | 44 ++++++ src/helpers/ByteHandler.cpp | 52 ++++++ src/helpers/CMakeLists.txt | 2 + src/helpers/DiskWriter.cpp | 5 +- src/kernels/Kernel.cpp | 28 ---- .../concrete/BivariateMaternFlexible.cpp | 2 +- .../concrete/BivariateMaternParsimonious.cpp | 2 +- .../BivariateSpacetimeMaternStationary.cpp | 2 +- .../concrete/TrivariateMaternParsimonious.cpp | 2 +- .../concrete/UnivariateExpNonGaussian.cpp | 2 +- .../concrete/UnivariateMaternDbeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaBeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaNu.cpp | 3 +- .../concrete/UnivariateMaternDdnuNu.cpp | 14 +- .../UnivariateMaternDdsigmaSquareBeta.cpp | 2 +- .../UnivariateMaternDdsigmaSquareNu.cpp | 2 +- src/kernels/concrete/UnivariateMaternDnu.cpp | 4 +- .../concrete/UnivariateMaternDsigmaSquare.cpp | 2 +- .../concrete/UnivariateMaternNonGaussian.cpp | 2 +- .../UnivariateMaternNuggetsStationary.cpp | 2 +- .../concrete/UnivariateMaternStationary.cpp | 2 +- .../UnivariateSpacetimeMaternStationary.cpp | 2 +- .../concrete/TestSyntheticGenerator.cpp | 63 ++++---- tests/heavy-tests/HeavyTests.cpp | 1 - 60 files changed, 907 insertions(+), 553 deletions(-) create mode 100644 examples/configurations/RunningWithDifferentConfigurations.cpp rename examples/configurations/{ConfigurationModule.cpp => SetupConfigurations.cpp} (88%) create mode 100644 inst/include/data-generators/LocationGenerator.hpp delete mode 100644 inst/include/data-generators/concrete/CSVDataGenerator.hpp create mode 100644 inst/include/data-loader/DataLoader.hpp create mode 100644 inst/include/data-loader/concrete/CSVLoader.hpp create mode 100644 inst/include/helpers/BasselFunction.hpp create mode 100644 inst/include/helpers/ByteHandler.hpp create mode 100644 src/data-generators/LocationGenerator.cpp create mode 100644 src/data-loader/CMakeLists.txt create mode 100644 src/data-loader/DataLoader.cpp create mode 100644 src/data-loader/concrete/CMakeLists.txt rename src/{data-generators/concrete/CSVDataGenerator.cpp => data-loader/concrete/CSVLoader.cpp} (61%) create mode 100644 src/helpers/BasselFunction.cpp create mode 100644 src/helpers/ByteHandler.cpp diff --git a/.gitignore b/.gitignore index 68debb78..4ab21a78 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ installdir/ # R files .Rhistory .RData +.RDataTmp # tar.gz files *.tar.gz diff --git a/CHANGELOG.md b/CHANGELOG.md index 15c11f29..488c1380 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Implemented a new changelog. - Introduced a benchmarking script. -- .gitignore file +- .gitignore file. +- More examples. ### Fixed - Resolved issues with MPI installation. @@ -24,6 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Adjusted CMake variables. - Revised the process of finding BLASPP and Catch2 libraries. - Updated doxygen documentation. +- Split the synthetic generator functions into BitHelper class and Locations generator class. +- Created a Bassel Function helper for kernels. +- Cleaned the code base for better readability. ### Removed - Eliminated non-stationary kernel support. diff --git a/config.sh b/config.sh index 04b62f5d..04ac419e 100755 --- a/config.sh +++ b/config.sh @@ -30,8 +30,20 @@ SHOW_WARNINGS="OFF" COMPILE_FLAGS="-Wl,--no-as-needed" DEVELOPER_WARNINGS="-Wno-dev" + +for arg in "$@" +do + case $arg in + --use-mkl) + echo -e "${GREEN}MKL as a BLA vendor${NC}" + BLAS_VENDOR="Intel10_64lp" + shift # Remove --use-mkl from processing + ;; + esac +done + # Parse command line options -while getopts ":tevhHi:cmspTw" opt; do +while getopts ":tevhHi:cmpTw" opt; do case $opt in i) ##### Define installation path ##### echo -e "${YELLOW}Installation path set to $OPTARG.${NC}" @@ -65,10 +77,6 @@ while getopts ":tevhHi:cmspTw" opt; do echo -e "${GREEN}printing make with details.${NC}" VERBOSE="ON" ;; - s) ##### Passing BLA vendor with mkl ##### - echo -e "${GREEN}MKL as a BLA vendor${NC}" - BLAS_VENDOR="Intel10_64lp" - ;; p) ##### Enabling packaging system for distribution ##### echo -e "${GREEN}CPACK enabled${NC}" PACKAGE=ON @@ -145,7 +153,7 @@ echo "" rm -rf bin/ mkdir -p bin/ -cmake $DEVELOPER_WARNINGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ +cmake "$DEVELOPER_WARNINGS" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCMAKE_BUILD_TYPE=RELEASE \ -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ -DBUILD_TESTS="${BUILDING_TESTS}" \ diff --git a/examples/configurations/CMakeLists.txt b/examples/configurations/CMakeLists.txt index 2c556898..7c68b9ee 100644 --- a/examples/configurations/CMakeLists.txt +++ b/examples/configurations/CMakeLists.txt @@ -10,7 +10,9 @@ # @date 2023-01-31 # Define an executable named "Example_Configurations". -add_executable(Example_Configurations ${CMAKE_CURRENT_SOURCE_DIR}/ConfigurationModule.cpp) +add_executable(Example_Configurations_Setup ${CMAKE_CURRENT_SOURCE_DIR}/SetupConfigurations.cpp) +add_executable(Example_Different_Configurations_Running ${CMAKE_CURRENT_SOURCE_DIR}/RunningWithDifferentConfigurations.cpp) # Link the executable with the ExaGeoStat library and other libraries. -target_link_libraries(Example_Configurations ${PROJECT_NAME}_INTERFACE) \ No newline at end of file +target_link_libraries(Example_Configurations_Setup ${PROJECT_NAME}_INTERFACE) +target_link_libraries(Example_Different_Configurations_Running ${PROJECT_NAME}_INTERFACE) \ No newline at end of file diff --git a/examples/configurations/RunningWithDifferentConfigurations.cpp b/examples/configurations/RunningWithDifferentConfigurations.cpp new file mode 100644 index 00000000..f8d8c94f --- /dev/null +++ b/examples/configurations/RunningWithDifferentConfigurations.cpp @@ -0,0 +1,75 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file RunningWithDifferentConfigurations.cpp + * @brief Demonstrates running ExaGeoStat with various configurations. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-01-04 +**/ + +#include +#include + +using namespace exageostat::configurations; +using namespace exageostat::api; +using namespace exageostat::hardware; +using namespace exageostat::dataunits; + +/** + * @brief Main entry point for the Data Generation & Data Modeling program. + * @details + * @return An integer indicating the success or failure of the program. + */ +int main() { + + // Create a new configurations object. + Configurations configurations; + int N = 16; + configurations.SetProblemSize(N); + configurations.SetKernelName("UnivariateMaternStationary"); + configurations.SetDenseTileSize(9); + configurations.SetMaxMleIterations(10); + configurations.SetTolerance(4); + std::vector lb{0.1, 0.1, 0.1}; + configurations.SetLowerBounds(lb); + + std::vector ub{5, 5, 5}; + configurations.SetUpperBounds(ub); + + std::vector initial_theta{1, 0.1, 0.5}; + configurations.SetInitialTheta(initial_theta); + + // Initialize the ExaGeoStat Hardware + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), + configurations.GetGPUsNumbers()); + // Load data by either read from file or create synthetic data. + std::unique_ptr> data; + ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); + // Modeling module. + ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); + + LOGGER("") + LOGGER("ANOTHER CONFIGURATIONS\n") + // Create a new configurations object. + Configurations configurations2; + N = 10; + configurations2.SetProblemSize(N); + configurations2.SetKernelName("UnivariateMaternStationary"); + configurations2.SetDenseTileSize(5); + configurations2.SetMaxMleIterations(2); + configurations2.SetTolerance(3); + configurations2.SetLowerBounds(lb); + configurations2.SetUpperBounds(ub); + configurations2.SetInitialTheta(initial_theta); + + // Load data by either read from file or create synthetic data. + ExaGeoStat::ExaGeoStatLoadData(hardware, configurations2, data); + // Modeling module. + ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations2, data); + + return 0; +} diff --git a/examples/configurations/ConfigurationModule.cpp b/examples/configurations/SetupConfigurations.cpp similarity index 88% rename from examples/configurations/ConfigurationModule.cpp rename to examples/configurations/SetupConfigurations.cpp index 975b0897..bb1f3c4e 100644 --- a/examples/configurations/ConfigurationModule.cpp +++ b/examples/configurations/SetupConfigurations.cpp @@ -4,10 +4,10 @@ // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). /** -* @file ConfigurationsMain.cpp +* @file SetupConfigurations.cpp * @brief Demonstrates how to use the Configurations class from the ExaGeoStat software package. * @details This file demonstrates how to use the Configurations class from the ExaGeoStat software package -* to obtain user-defined configurations for generating synthetic data. +* to obtain user-defined configurations. * @version 1.0.0 * @author Mahmoud ElKarargy * @date 2023-01-31 @@ -27,8 +27,8 @@ using namespace exageostat::configurations; /** * @brief The main function of the program. * - * This function demonstrates how to use the SyntheticDataConfigurations class from the ExaGeoStat software package - * to obtain user-defined configurations for generating synthetic data. + * This function demonstrates how to use the Configurations class from the ExaGeoStat software package + * to obtain user-defined configurations. * * @param[in] argc The number of command line arguments. * @param[in] argv The command line arguments. @@ -36,12 +36,11 @@ using namespace exageostat::configurations; */ int main(int argc, char **argv) { - // Create an instance of the SyntheticDataConfigurations class with user-defined configurations. + // Create an instance of the Configurations class with user-defined configurations. Configurations configurations; configurations.InitializeArguments(argc, argv); LOGGER("** These are some examples of the common arguments needed between all modules of ExaGeoStat **") - // Obtain user-defined configurations and print them to the console. int n = configurations.GetProblemSize(); if (n != 0) { @@ -101,5 +100,6 @@ int main(int argc, char **argv) { } VERBOSE("VERBOSE ACTIVATED") + return 0; } \ No newline at end of file diff --git a/inst/include/api/ExaGeoStat.hpp b/inst/include/api/ExaGeoStat.hpp index 9a1aa9b7..338713be 100644 --- a/inst/include/api/ExaGeoStat.hpp +++ b/inst/include/api/ExaGeoStat.hpp @@ -49,7 +49,7 @@ namespace exageostat::api { * @param[in] aConfigurations Reference to Configurations object containing user input data. * @param[in] aData Reference to an ExaGeoStatData object containing needed descriptors, and locations. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. - * @return void + * @return the last optimum value of MLE. * */ static T ExaGeoStatDataModeling(const hardware::ExaGeoStatHardware &aHardware, diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index 935c78a7..8bd8e341 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -127,6 +127,15 @@ namespace exageostat::common { HICMA_DESCRIPTOR = 1 }; + /** + * @enum Descriptor Type + * @brief Enum denoting the Descriptor Type. + */ + enum DataGeneratorType { + SYNTHETIC = 0, + CSV_FILE = 1 + }; + /** * @enum Descriptor Name * @brief Enum denoting all Descriptors Names. diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index 47968bc2..1c7658a4 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -75,10 +75,10 @@ namespace exageostat::configurations { Configurations(); /** - * @brief Virtual destructor to allow calls to the correct concrete destructor. + * @brief destructor to allow calls to the correct concrete destructor. * */ - virtual ~Configurations() = default; + ~Configurations() = default; /** * @brief Initialize the module arguments. diff --git a/inst/include/data-generators/DataGenerator.hpp b/inst/include/data-generators/DataGenerator.hpp index 130065e3..06aed2c2 100644 --- a/inst/include/data-generators/DataGenerator.hpp +++ b/inst/include/data-generators/DataGenerator.hpp @@ -14,8 +14,6 @@ #ifndef EXAGEOSTAT_CPP_DATAGENERATOR_HPP #define EXAGEOSTAT_CPP_DATAGENERATOR_HPP -#include - #include #include @@ -64,10 +62,8 @@ namespace exageostat::generators { virtual ~DataGenerator(); protected: - /// Used bool identifying type of generation. - static bool mIsSynthetic; - /// Used bool identifying type of generation. - static bool mIsCSV; + /// Used enum for data generators types. + static common::DataGeneratorType aDataGeneratorType; }; /** diff --git a/inst/include/data-generators/LocationGenerator.hpp b/inst/include/data-generators/LocationGenerator.hpp new file mode 100644 index 00000000..a041e1e6 --- /dev/null +++ b/inst/include/data-generators/LocationGenerator.hpp @@ -0,0 +1,73 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file LocationGenerator.hpp + * @brief Generates and manages spatial locations for ExaGeoStat. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-02-04 +**/ + +#ifndef EXAGEOSTATCPP_LOCATIONGENERATOR_HPP +#define EXAGEOSTATCPP_LOCATIONGENERATOR_HPP + +#include +#include + +namespace exageostat::generators { + + /** + * @class LocationGenerator + * @brief Generates spatial locations based on given parameters. + * @tparam T Data Type: float or double + * + */ + template + class LocationGenerator { + + public: + + /** + * @brief Generates the data locations. + * @details This method generates the X, Y, and Z variables used to define the locations of the data points. + * @param[in] aN The number of data points. + * @param[in] aTimeSlot The time slot. + * @param[in] aDimension The dimension of the locations. + * @param[out] aLocations Reference to the Locations object where the generated data will be stored. + * @return void + * + */ + static void GenerateLocations(const int &aN, const int &aTimeSlot, const common::Dimension &aDimension, dataunits::Locations &aLocations); + + /** + * @brief Generate uniform distribution between rangeLow , rangeHigh. + * @param[in] aRangeLow The Lower range. + * @param[in] aRangeHigh The Higher range. + * @return The scaled uniform distribution between the two bounds. + * + */ + static T UniformDistribution(const T &aRangeLow, const T &aRangeHigh); + + /** + * @brief Sort locations in Morton order (input points must be in [0;1]x[0;1] square]). + * @param[in] aN The problem size divided by P-Grid. + * @param[in] aDimension Dimension of locations. + * @param[in,out] aLocations Locations to be sorted. + * @return void + * + */ + static void SortLocations(const int &aN, const common::Dimension &aDimension, dataunits::Locations &aLocations); + }; + + /** + * @brief Instantiates the Data Generator class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(LocationGenerator) +}//namespace exageostat + +#endif //EXAGEOSTATCPP_LOCATIONGENERATOR_HPP diff --git a/inst/include/data-generators/concrete/CSVDataGenerator.hpp b/inst/include/data-generators/concrete/CSVDataGenerator.hpp deleted file mode 100644 index cb5220b3..00000000 --- a/inst/include/data-generators/concrete/CSVDataGenerator.hpp +++ /dev/null @@ -1,97 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file CSVDataGenerator.hpp - * @brief A class for generating synthetic data. - * @version 1.0.1 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @date 2023-02-14 -**/ - -#ifndef EXAGEOSTAT_CPP_CSVDATAGENERATOR_HPP -#define EXAGEOSTAT_CPP_CSVDATAGENERATOR_HPP - -#include - -namespace exageostat::generators::csv { - - /** - * @class CSVDataGenerator - * @brief A class for creating data by reading CSV files. - * @tparam T Data Type: float or double - */ - template - class CSVDataGenerator : public DataGenerator { - public: - - /** - * @brief Get a pointer to the singleton instance of the CSVDataGenerator class. - * @return A pointer to the instance of the CSVDataGenerator class. - * - */ - static CSVDataGenerator *GetInstance(); - - /** - * @brief Creates the data by reading a CSV file. - * @copydoc DataGenerator::CreateData() - * - */ - std::unique_ptr> CreateData(exageostat::configurations::Configurations &, - const exageostat::hardware::ExaGeoStatHardware &aHardware, - exageostat::kernels::Kernel &aKernel) override; - - /** - * @brief Reads CSV files containing 2D, 3D, or ST locations, and measurements vector. - * @param aConfigurations Reference to the Configurations object. - * @param aMeasurementsMatrix Reference to the Measurement matrix to be filled with read data. - * @param aXLocations Reference to the location's x coordinates matrix to be filled with read data. - * @param aYLocations Reference to the location's y coordinates matrix to be filled with read data. - * @param aZLocations Reference to the location's z coordinates matrix to be filled with read data. - * @param[in] aP the P value of the kernel multiplied by time slot. - * @return void - */ - void ReadData(exageostat::configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, - std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, - const int &aP); - - /** - * @brief Release the singleton instance of the CSVDataGenerator class. - * @return void - * - */ - static void ReleaseInstance(); - - private: - /** - * @brief Constructor for the CSVDataGenerator class. - * @return void - * - */ - CSVDataGenerator() = default; - - /** - * @brief Default destructor. - * - */ - ~CSVDataGenerator() override = default; - - /** - * @brief Pointer to the singleton instance of the CSVDataGenerator class. - * - */ - static CSVDataGenerator *mpInstance; - - }; - - /** - * @brief Instantiates the CSV Data Generator class for float and double types. - * @tparam T Data Type: float or double - * - */ - EXAGEOSTAT_INSTANTIATE_CLASS(CSVDataGenerator) -} -#endif //EXAGEOSTAT_CPP_CSVDATAGENERATOR_HPP \ No newline at end of file diff --git a/inst/include/data-generators/concrete/SyntheticGenerator.hpp b/inst/include/data-generators/concrete/SyntheticGenerator.hpp index e73e38be..fc9b507d 100644 --- a/inst/include/data-generators/concrete/SyntheticGenerator.hpp +++ b/inst/include/data-generators/concrete/SyntheticGenerator.hpp @@ -38,19 +38,6 @@ namespace exageostat::generators::synthetic { */ static SyntheticGenerator *GetInstance(); - /** - * @brief Generates the data locations. - * @details This method generates the X, Y, and Z variables used to define the locations of the data points. - * @param[in] aN The number of data points. - * @param[in] aTimeSlot The time slot. - * @param[in] aDimension The dimension of the locations. - * @param[out] aLocations Reference to the Locations object where the generated data will be stored. - * @return void - * - */ - void GenerateLocations(const int &aN, const int &aTimeSlot, const common::Dimension &aDimension, - dataunits::Locations &aLocations); - /** * @brief Creates the data by synthetically generating it. * @copydoc DataGenerator::CreateData() @@ -61,51 +48,6 @@ namespace exageostat::generators::synthetic { const exageostat::hardware::ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) override; - /** - * @brief Generate uniform distribution between rangeLow , rangeHigh. - * @param[in] aRangeLow The Lower range. - * @param[in] aRangeHigh The Higher range. - * @return The scaled uniform distribution between the two bounds. - * - */ - static T UniformDistribution(const T &aRangeLow, const T &aRangeHigh); - - /** - * @brief Sort locations in Morton order (input points must be in [0;1]x[0;1] square]). - * @param[in] aN The problem size divided by P-Grid. - * @param[in] aDimension Dimension of locations. - * @param[in,out] aLocations Locations to be sorted. - * @return void - * - */ - void - SortLocations(const int &aN, const common::Dimension &aDimension, dataunits::Locations &aLocations); - - /** - * @brief Spread bits by three spaces. - * @param[in] aInputByte The input 64 bit to be spread. - * @return The byte after being spread. - * - */ - static uint64_t SpreadBits(uint64_t aInputByte); - - /** - * @brief Reverse Spread bits operation. - * @param[in] aInputByte The input spread 64 bit to be compacted. - * @return The byte after being compacted. - * - */ - static uint64_t ReverseSpreadBits(uint64_t aInputByte); - - /** - * @brief Compares two Unit64 values - * @param[in] aFirstValue Constant reference to the first input 64 bit value. - * @param[in] aSecondValue Constant reference to the second input 64 bit value. - * @return True if the second value is bigger than the first value, false otherwise. - * - */ - static bool CompareUint64(const uint64_t &aFirstValue, const uint64_t &aSecondValue); - /** * @brief Release the singleton instance of the SyntheticGenerator class. * @return void diff --git a/inst/include/data-loader/DataLoader.hpp b/inst/include/data-loader/DataLoader.hpp new file mode 100644 index 00000000..d9bd5d26 --- /dev/null +++ b/inst/include/data-loader/DataLoader.hpp @@ -0,0 +1,67 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file DataLoader.hpp + * @brief Manages data loading operations for ExaGeoStat. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-04 +**/ + +#ifndef EXAGEOSTATCPP_DATALOADER_HPP +#define EXAGEOSTATCPP_DATALOADER_HPP + +#include + +namespace exageostat::dataLoader { + + /** + * @class DataLoader + * @brief Extends DataGenerator to include data loading functionalities. + * @tparam T Data Type: float or double + * + */ + template + class DataLoader : public generators::DataGenerator { + + public: + + /** + * @brief Creates the data by synthetically generating it. + * @copydoc DataGenerator::CreateData() + * + */ + std::unique_ptr> + CreateData(exageostat::configurations::Configurations &aConfigurations, + const exageostat::hardware::ExaGeoStatHardware &aHardware, + exageostat::kernels::Kernel &aKernel) override; + + /** + * @brief Reads data from external sources into ExaGeoStat format. + * @param aConfigurations Configuration settings for data loading. + * @param aMeasurementsMatrix Vector to store measurement values. + * @param aXLocations Vector to store X coordinates of locations. + * @param aYLocations Vector to store Y coordinates of locations. + * @param aZLocations Vector to store Z coordinates of locations (if applicable). + * @param aP Partition index for distributed data loading. + */ + virtual void + ReadData(exageostat::configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, + std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, + const int &aP) = 0; + + }; + + /** + * @brief Instantiates the Synthetic Data Generator class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(DataLoader) +} // namespace exageostat + +#endif //EXAGEOSTATCPP_DATALOADER_HPP diff --git a/inst/include/data-loader/concrete/CSVLoader.hpp b/inst/include/data-loader/concrete/CSVLoader.hpp new file mode 100644 index 00000000..f2411178 --- /dev/null +++ b/inst/include/data-loader/concrete/CSVLoader.hpp @@ -0,0 +1,84 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file CSVLoader.hpp + * @brief A class for generating synthetic data. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-04 +**/ + +#ifndef EXAGEOSTAT_CPP_CSVDATALOADER_HPP +#define EXAGEOSTAT_CPP_CSVDATALOADER_HPP + +#include +#include + +namespace exageostat::dataLoader::csv { + + /** + * @class CSVLoader + * @brief A class for creating data by reading CSV files. + * @tparam T Data Type: float or double + */ + template + class CSVLoader : public DataLoader { + public: + + /** + * @brief Get a pointer to the singleton instance of the CSVLoader class. + * @return A pointer to the instance of the CSVLoader class. + * + */ + static CSVLoader *GetInstance(); + + /** + * @brief Reads data from external sources into ExaGeoStat format. + * @copydoc DataLoader::ReadData() + * + */ + void ReadData(exageostat::configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, + std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, + const int &aP) override; + + /** + * @brief Release the singleton instance of the CSVLoader class. + * @return void + * + */ + static void ReleaseInstance(); + + private: + /** + * @brief Constructor for the CSVLoader class. + * @return void + * + */ + CSVLoader() = default; + + /** + * @brief Default destructor. + * + */ + ~CSVLoader() override = default; + + /** + * @brief Pointer to the singleton instance of the CSVLoader class. + * + */ + static CSVLoader *mpInstance; + + }; + + /** + * @brief Instantiates the CSV Data Generator class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(CSVLoader) +} +#endif //EXAGEOSTAT_CPP_CSVDATALOADER_HPP \ No newline at end of file diff --git a/inst/include/data-units/DescriptorData.hpp b/inst/include/data-units/DescriptorData.hpp index d80e03b6..cb5f549d 100644 --- a/inst/include/data-units/DescriptorData.hpp +++ b/inst/include/data-units/DescriptorData.hpp @@ -49,7 +49,7 @@ namespace exageostat::dataunits { /** * @brief Destructor for DescriptorData. */ - virtual ~DescriptorData(); + ~DescriptorData(); /** * @brief Get the base descriptor. diff --git a/inst/include/data-units/Locations.hpp b/inst/include/data-units/Locations.hpp index 032be1f9..ebfb558b 100644 --- a/inst/include/data-units/Locations.hpp +++ b/inst/include/data-units/Locations.hpp @@ -43,10 +43,10 @@ namespace exageostat::dataunits { Locations(const Locations &aLocations) = default; /** - * @brief Virtual destructor to allow calls to the correct concrete destructor. + * @brief destructor for Locations. * */ - virtual ~Locations(); + ~Locations(); /** * @brief Setter for LocationX. diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index 8ed894a5..b7e5c236 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -37,7 +37,7 @@ namespace exageostat::hardware { /** * @brief Destructor for ExaGeoStatHardware. */ - virtual ~ExaGeoStatHardware(); + ~ExaGeoStatHardware(); /** * @brief Get the Chameleon hardware context. diff --git a/inst/include/helpers/BasselFunction.hpp b/inst/include/helpers/BasselFunction.hpp new file mode 100644 index 00000000..62430f2a --- /dev/null +++ b/inst/include/helpers/BasselFunction.hpp @@ -0,0 +1,69 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file BasselFunction.hpp + * @brief This file contains the BasselFunction class which provides methods for computing derivatives of the modified Bessel function of the second kind. These functions are crucial in statistical and mathematical computations, especially in fields such as geostatistics and spatial analysis. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2023-01-24 +**/ + +#ifndef EXAGEOSTATCPP_BASSELFUNCTION_HPP +#define EXAGEOSTATCPP_BASSELFUNCTION_HPP + +#include + +namespace exageostat::helpers { + + /** + * @class BasselFunction + * @brief The BasselFunction class provides methods for computing various derivatives of the modified Bessel function of the second kind, \( K_{\nu} \). This class is templated to support both float and double data types, enabling precision-based computations as required by different applications. + * @tparam T Data Type: float or double + * + */ + template + class BasselFunction { + + public: + /** + * @brief Calculates the derivative of the modified Bessel function of the second kind (K_nu) with respect to its order, evaluated at input_value and order aOrder. + * @param[in] aOrder The order of the Bessel function. + * @param[in] aInputValue The input value at which to evaluate the derivative. + * @return The value of the derivative of K_nu with respect to its order, evaluated at input_value and order aOrder. + * + */ + static T CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue); + + /** + * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. + * @param[in] aOrder The order of the Bessel function. + * @param[in] aInputValue The input value at which to evaluate the second derivative. + * @return The value of the second derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. + * + */ + static T CalculateSecondDerivativeBesselNu(const T &aOrder, const T &aInputValue); + + /** + * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. + * @param[in] aOrder The order of the Bessel function. + * @param[in] aInputValue The input value at which to evaluate the derivative. + * @return The value of the derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. + * + */ + static T CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aInputValue); + + }; + + /** + * @brief Instantiates the DiskWriter class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(BasselFunction) +} + +#endif //EXAGEOSTATCPP_BASSELFUNCTION_HPP diff --git a/inst/include/helpers/ByteHandler.hpp b/inst/include/helpers/ByteHandler.hpp new file mode 100644 index 00000000..cb071f4c --- /dev/null +++ b/inst/include/helpers/ByteHandler.hpp @@ -0,0 +1,48 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ByteHandler.hpp + * @brief Implementation of byte manipulation functions for ExaGeoStat. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-01-24 +**/ + +#ifndef EXAGEOSTATCPP_BYTEHANDLER_HPP +#define EXAGEOSTATCPP_BYTEHANDLER_HPP + +#include + +namespace exageostat::helpers { + + /** + * @brief Spread bits by three spaces. + * @param[in] aInputByte The input 64 bit to be spread. + * @return The byte after being spread. + * + */ + uint64_t SpreadBits(uint64_t aInputByte); + + /** + * @brief Reverse Spread bits operation. + * @param[in] aInputByte The input spread 64 bit to be compacted. + * @return The byte after being compacted. + * + */ + uint64_t ReverseSpreadBits(uint64_t aInputByte); + + /** + * @brief Compares two Unit64 values + * @param[in] aFirstValue Constant reference to the first input 64 bit value. + * @param[in] aSecondValue Constant reference to the second input 64 bit value. + * @return True if the second value is bigger than the first value, false otherwise. + * + */ + bool CompareUint64(const uint64_t &aFirstValue, const uint64_t &aSecondValue); + +} +#endif //EXAGEOSTATCPP_BYTEHANDLER_HPP diff --git a/inst/include/helpers/DiskWriter.hpp b/inst/include/helpers/DiskWriter.hpp index 822e86a4..4e4a0f12 100644 --- a/inst/include/helpers/DiskWriter.hpp +++ b/inst/include/helpers/DiskWriter.hpp @@ -6,10 +6,10 @@ /** * @file DiskWriter.hpp * @brief Contains the definition of the DiskWriter class for writing data to disk. - * @version 1.0.0 + * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-06-08 + * @date 2023-01-24 **/ #ifndef EXAGEOSTATCPP_DISKWRITER_HPP diff --git a/inst/include/kernels/Kernel.hpp b/inst/include/kernels/Kernel.hpp index 685b1d19..6fa85fa6 100644 --- a/inst/include/kernels/Kernel.hpp +++ b/inst/include/kernels/Kernel.hpp @@ -30,6 +30,7 @@ extern "C" { #include #include #include +#include /** * @def EARTH_RADIUS @@ -100,33 +101,6 @@ namespace exageostat::kernels { dataunits::Locations &aLocation2, dataunits::Locations &aLocation3, T *apLocalTheta, const int &aDistanceMetric) = 0; - /** - * @brief Calculates the derivative of the modified Bessel function of the second kind (K_nu) with respect to its order, evaluated at input_value and order aOrder. - * @param[in] aOrder The order of the Bessel function. - * @param[in] aInputValue The input value at which to evaluate the derivative. - * @return The value of the derivative of K_nu with respect to its order, evaluated at input_value and order aOrder. - * - */ - static T CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue); - - /** - * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. - * @param[in] aOrder The order of the Bessel function. - * @param[in] aInputValue The input value at which to evaluate the second derivative. - * @return The value of the second derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. - * - */ - static T CalculateSecondDerivativeBesselNu(const T &aOrder, const T &aInputValue); - - /** - * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. - * @param[in] aOrder The order of the Bessel function. - * @param[in] aInputValue The input value at which to evaluate the derivative. - * @return The value of the derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. - * - */ - static T CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aInputValue); - /** * @brief Returns the value of the parameter P used by the kernel function. * @return The value of P (Variables Number). diff --git a/inst/include/prediction/Prediction.hpp b/inst/include/prediction/Prediction.hpp index e96742bc..5e7a42fb 100644 --- a/inst/include/prediction/Prediction.hpp +++ b/inst/include/prediction/Prediction.hpp @@ -29,16 +29,6 @@ namespace exageostat::prediction { class Prediction { public: - /** - * @brief Default constructor for Prediction class. - */ - Prediction() = default; - - /** - * @brief Default destructor for Prediction class. - */ - ~Prediction() = default; - /** * @brief Takes care of calling the MSPE function, and the appropriate auxiliary function. * @param[in] aHardware Reference to Hardware configuration for the ExaGeoStat solver. @@ -48,10 +38,10 @@ namespace exageostat::prediction { * @param[in] aKernel Reference to the kernel object to use. * @return */ - void PredictMissingData(const exageostat::hardware::ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, - exageostat::configurations::Configurations &aConfigurations, T *apMeasurementsMatrix, - const kernels::Kernel &aKernel); + static void PredictMissingData(const exageostat::hardware::ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, + exageostat::configurations::Configurations &aConfigurations, + T *apMeasurementsMatrix, const kernels::Kernel &aKernel); /** * @brief Initializes needed pointers for prediction. @@ -66,12 +56,13 @@ namespace exageostat::prediction { * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ - void InitializePredictionArguments(exageostat::configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, - std::unique_ptr> &aLinearAlgebraSolver, - T *apZObs, T *apZActual, exageostat::dataunits::Locations &aMissLocation, - exageostat::dataunits::Locations &aObsLocation, T *apMeasurementsMatrix, - const int &aP); + static void InitializePredictionArguments(exageostat::configurations::Configurations &aConfigurations, + std::unique_ptr> &aData, + std::unique_ptr> &aLinearAlgebraSolver, + T *apZObs, T *apZActual, + exageostat::dataunits::Locations &aMissLocation, + exageostat::dataunits::Locations &aObsLocation, + T *apMeasurementsMatrix, const int &aP); }; diff --git a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp index ad07828f..d9ebf857 100644 --- a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp +++ b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp @@ -28,16 +28,6 @@ namespace exageostat::prediction { class PredictionAuxiliaryFunctions { public: - /** - * @brief Default constructor for PredictionAuxiliaryFunctions - */ - PredictionAuxiliaryFunctions() = default; - - /** - * @brief Default destructor for PredictionAuxiliaryFunctions - */ - ~PredictionAuxiliaryFunctions() = default; - /** * @brief implements the Inverse Distance Weighting (IDW) interpolation method * for predicting missing values based on available observed values. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ecad6074..6fde05af 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/helpers) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/hardware) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/prediction) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/results) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-loader) # Set the name of the library to be created. set(LIB_NAME ${PROJECT_NAME}) diff --git a/src/api/ExaGeoStat.cpp b/src/api/ExaGeoStat.cpp index d8a7d24a..7a03121f 100644 --- a/src/api/ExaGeoStat.cpp +++ b/src/api/ExaGeoStat.cpp @@ -99,12 +99,11 @@ void ExaGeoStat::ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, Co T *apMeasurementsMatrix) { LOGGER("** ExaGeoStat data Prediction **") - Prediction predictor; // Register and create a kernel object kernels::Kernel *pKernel = plugins::PluginRegistry>::Create(aConfigurations.GetKernelName(), aConfigurations.GetTimeSlot()); // Add the data prediction arguments. aConfigurations.InitializeDataPredictionArguments(); - predictor.PredictMissingData(aHardware, aData, aConfigurations, apMeasurementsMatrix, *pKernel); + Prediction::PredictMissingData(aHardware, aData, aConfigurations, apMeasurementsMatrix, *pKernel); delete pKernel; } \ No newline at end of file diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index b15e5541..1b8ab14d 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -67,6 +67,7 @@ Configurations::Configurations() { SetDistanceMetric(common::EUCLIDEAN_DISTANCE); SetAccuracy(0); SetIsNonGaussian(false); + mIsThetaInit = false; } diff --git a/src/data-generators/CMakeLists.txt b/src/data-generators/CMakeLists.txt index 67966b9e..4deb9b32 100644 --- a/src/data-generators/CMakeLists.txt +++ b/src/data-generators/CMakeLists.txt @@ -8,7 +8,7 @@ # @version 1.0.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah -# @date 2023-02-14 +# @date 2024-02-04 # Add subdirectories for concrete implementations add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/concrete) @@ -16,6 +16,7 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/concrete) # Add source files to the parent scope set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/DataGenerator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/LocationGenerator.cpp ${SOURCES} PARENT_SCOPE ) diff --git a/src/data-generators/DataGenerator.cpp b/src/data-generators/DataGenerator.cpp index 548185a0..a0515b68 100644 --- a/src/data-generators/DataGenerator.cpp +++ b/src/data-generators/DataGenerator.cpp @@ -13,28 +13,39 @@ #include #include -#include +#include #include using namespace exageostat::generators; +using namespace exageostat::dataLoader::csv; using namespace exageostat::generators::synthetic; -using namespace exageostat::generators::csv; using namespace exageostat::dataunits; using namespace exageostat::configurations; +using namespace exageostat::common; template std::unique_ptr> DataGenerator::CreateGenerator(Configurations &apConfigurations) { // Check the used Data generation method, whether it's synthetic or real. - mIsSynthetic = apConfigurations.GetIsSynthetic(); - mIsCSV = apConfigurations.GetIsCSV(); - results::Results::GetInstance()->SetIsSynthetic(mIsSynthetic); + if(apConfigurations.GetIsSynthetic() && apConfigurations.GetIsCSV()){ + throw std::domain_error("Please activate either the synthetic or the CSV file for data generation, but not both."); + } + if(!apConfigurations.GetIsSynthetic() && !apConfigurations.GetIsCSV()){ + throw std::domain_error("Please activate either the synthetic or the CSV file for data generation"); + } + if(apConfigurations.GetIsSynthetic()){ + aDataGeneratorType = SYNTHETIC; + } + else if(apConfigurations.GetIsCSV()){ + aDataGeneratorType = CSV_FILE; + } + results::Results::GetInstance()->SetIsSynthetic(apConfigurations.GetIsSynthetic()); // Return DataGenerator unique pointer of Synthetic type - if (mIsSynthetic) { + if (aDataGeneratorType == SYNTHETIC) { return std::unique_ptr>(SyntheticGenerator::GetInstance()); - } else if (mIsCSV) { - return std::unique_ptr>(CSVDataGenerator::GetInstance()); + } else if (aDataGeneratorType == CSV_FILE) { + return std::unique_ptr>(CSVLoader::GetInstance()); } else { throw std::runtime_error("Data Loading for this file type is unsupported for now"); } @@ -43,15 +54,14 @@ std::unique_ptr> DataGenerator::CreateGenerator(Configuratio template DataGenerator::~DataGenerator() { // Return DataGenerator unique pointer of Synthetic type - if (mIsSynthetic) { + if (aDataGeneratorType == SYNTHETIC) { SyntheticGenerator::GetInstance()->ReleaseInstance(); - } else if (mIsCSV) { - CSVDataGenerator::GetInstance()->ReleaseInstance(); + } else if (aDataGeneratorType == CSV_FILE) { + CSVLoader::GetInstance()->ReleaseInstance(); } else { std::cerr << "Data Loading for this file type is unsupported for now" << std::endl; std::exit(1); } } -template bool DataGenerator::mIsSynthetic = true; -template bool DataGenerator::mIsCSV = false; +template DataGeneratorType DataGenerator::aDataGeneratorType = common::SYNTHETIC; diff --git a/src/data-generators/LocationGenerator.cpp b/src/data-generators/LocationGenerator.cpp new file mode 100644 index 00000000..47acd7fe --- /dev/null +++ b/src/data-generators/LocationGenerator.cpp @@ -0,0 +1,122 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file LocationGenerator.cpp + * @brief Generates and manages spatial locations for ExaGeoStat. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-02-04 +**/ + +#include +#include + +#include +#include + +using namespace exageostat::generators; +using namespace exageostat::common; +using namespace exageostat::dataunits; +using namespace exageostat::helpers; + +template void LocationGenerator::GenerateLocations(const int &aN, const int &aTimeSlot, const Dimension &aDimension, Locations &aLocations) { + + aLocations.SetSize(aN); + int index = 0; + aLocations.SetDimension(aDimension); + + int rootN; + if (aDimension == Dimension3D) { + //Cubic root. + rootN = ceil(cbrt(aN)); + } else { + //Square root. + rootN = ceil(sqrt(aN)); + } + + int *grid = new int[rootN](); + for (auto i = 0; i < rootN; i++) { + grid[i] = i + 1; + } + T range_low = -0.4, range_high = 0.4; + + for (auto i = 0; i < rootN && index < aN; i++) { + for (auto j = 0; j < rootN && index < aN; j++) { + if (aDimension == Dimension3D) { + for (auto k = 0; k < rootN && index < aN; k++) { + aLocations.GetLocationX()[index] = + (grid[i] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; + aLocations.GetLocationY()[index] = + (grid[j] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; + aLocations.GetLocationZ()[index] = + (grid[k] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; + index++; + } + } else { + aLocations.GetLocationX()[index] = + (grid[i] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; + aLocations.GetLocationY()[index] = + (grid[j] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; + if (aDimension == DimensionST) { + aLocations.GetLocationZ()[index] = 1.0; + } + index++; + } + } + } + delete[] grid; + if (aDimension != DimensionST) { + SortLocations(aN, aDimension, aLocations); + } else { + for (auto i = 0; i < aN; i++) { + aLocations.GetLocationX()[i] = aLocations.GetLocationX()[i]; + aLocations.GetLocationY()[i] = aLocations.GetLocationY()[i]; + aLocations.GetLocationZ()[i] = (T) (i / aTimeSlot + 1); + } + } +} + +template +T LocationGenerator::UniformDistribution(const T &aRangeLow, const T &aRangeHigh) { + T myRand = (T) rand() / (T) (1.0 + RAND_MAX); + T range = aRangeHigh - aRangeLow; + return (myRand * range) + aRangeLow; +} + +template +void +LocationGenerator::SortLocations(const int &aN, const Dimension &aDimension, Locations &aLocations) { + + // Some sorting, required by spatial statistics code + uint16_t x, y, z; + uint64_t vectorZ[aN]; + + // Encode data into vector z + for (auto i = 0; i < aN; i++) { + x = (uint16_t)(aLocations.GetLocationX()[i] * (double) UINT16_MAX + .5); + y = (uint16_t)(aLocations.GetLocationY()[i] * (double) UINT16_MAX + .5); + if (aDimension != Dimension2D) { + z = (uint16_t)(aLocations.GetLocationZ()[i] * (double) UINT16_MAX + .5); + } else { + z = (uint16_t) 0.0; + } + vectorZ[i] = (SpreadBits(z) << 2) + (SpreadBits(y) << 1) + SpreadBits(x); + } + // Sort vector z + std::sort(vectorZ, vectorZ + aN, CompareUint64); + + // Decode data from vector z + for (auto i = 0; i < aN; i++) { + x = ReverseSpreadBits(vectorZ[i] >> 0); + y = ReverseSpreadBits(vectorZ[i] >> 1); + z = ReverseSpreadBits(vectorZ[i] >> 2); + aLocations.GetLocationX()[i] = (double) x / (double) UINT16_MAX; + aLocations.GetLocationY()[i] = (double) y / (double) UINT16_MAX; + if (aDimension == Dimension3D) { + aLocations.GetLocationZ()[i] = (double) z / (double) UINT16_MAX; + } + } +} \ No newline at end of file diff --git a/src/data-generators/concrete/CMakeLists.txt b/src/data-generators/concrete/CMakeLists.txt index 55d821bb..5be7a228 100644 --- a/src/data-generators/concrete/CMakeLists.txt +++ b/src/data-generators/concrete/CMakeLists.txt @@ -13,8 +13,6 @@ # Add source files to the parent scope set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/SyntheticGenerator.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/CSVDataGenerator.cpp - ${SOURCES} PARENT_SCOPE ) \ No newline at end of file diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index e2de00a9..0ae3acd5 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -13,15 +13,12 @@ **/ #include +#include using namespace exageostat::generators::synthetic; using namespace exageostat::dataunits; -using namespace exageostat::hardware; using namespace exageostat::common; using namespace exageostat::configurations; -using namespace exageostat::kernels; -using namespace exageostat::helpers; -using namespace exageostat::linearAlgebra; template SyntheticGenerator *SyntheticGenerator::GetInstance() { @@ -50,11 +47,11 @@ SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aC aConfigurations.SetInitialTheta(aConfigurations.GetInitialTheta()); // Generate Locations phase - GenerateLocations(n, aConfigurations.GetTimeSlot(), aConfigurations.GetDimension(), *locations); + LocationGenerator::GenerateLocations(n, aConfigurations.GetTimeSlot(), aConfigurations.GetDimension(), *locations); data->SetLocations(*locations); // Generate Descriptors phase - auto linear_algebra_solver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); + auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); linear_algebra_solver->GenerateSyntheticData(aConfigurations, aHardware, data, aKernel); if (aConfigurations.GetLogger()) { @@ -66,7 +63,7 @@ SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aC CHAMELEON_Desc2Lap(ChamUpperLower, data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc, pMatrix, aConfigurations.GetProblemSize()); if ( CHAMELEON_Comm_rank == 0 ){ - DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + helpers::DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), aConfigurations.GetProblemSize(), aConfigurations.GetVariablesNumber(), path, *data->GetLocations()); @@ -74,7 +71,7 @@ SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aC delete[] pMatrix; #else std::string path = aConfigurations.GetLoggerPath(); - DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + helpers::DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), aConfigurations.GetProblemSize(), aKernel.GetVariablesNumber(), path, *data->GetLocations()); @@ -88,142 +85,6 @@ SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aC return data; } -template -void SyntheticGenerator::GenerateLocations(const int &aN, const int &aTimeSlot, const Dimension &aDimension, - Locations &aLocations) { - - aLocations.SetSize(aN); - int index = 0; - aLocations.SetDimension(aDimension); - - int rootN; - if (aDimension == Dimension3D) { - //Cubic root. - rootN = ceil(cbrt(aN)); - } else { - //Square root. - rootN = ceil(sqrt(aN)); - } - - int *grid = new int[rootN](); - for (auto i = 0; i < rootN; i++) { - grid[i] = i + 1; - } - - T range_low = -0.4, range_high = 0.4; - - for (auto i = 0; i < rootN && index < aN; i++) { - for (auto j = 0; j < rootN && index < aN; j++) { - if (aDimension == Dimension3D) { - for (auto k = 0; k < rootN && index < aN; k++) { - aLocations.GetLocationX()[index] = - (grid[i] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; - aLocations.GetLocationY()[index] = - (grid[j] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; - aLocations.GetLocationZ()[index] = - (grid[k] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; - index++; - } - } else { - aLocations.GetLocationX()[index] = (grid[i] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; - aLocations.GetLocationY()[index] = (grid[j] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; - if (aDimension == DimensionST) { - aLocations.GetLocationZ()[index] = 1.0; - } - index++; - } - } - } - delete[] grid; - if (aDimension != DimensionST) { - SortLocations(aN, aDimension, aLocations); - } else { - for (auto i = 0; i < aN; i++) { - aLocations.GetLocationX()[i] = aLocations.GetLocationX()[i]; - aLocations.GetLocationY()[i] = aLocations.GetLocationY()[i]; - aLocations.GetLocationZ()[i] = (T) (i / aTimeSlot + 1); - } - } -} - -template -T SyntheticGenerator::UniformDistribution(const T &aRangeLow, const T &aRangeHigh) { - T myRand = (T) rand() / (T) (1.0 + RAND_MAX); - T range = aRangeHigh - aRangeLow; - return (myRand * range) + aRangeLow; -} - -template -void SyntheticGenerator::SortLocations(const int &aN, const Dimension &aDimension, Locations &aLocations) { - - // Some sorting, required by spatial statistics code - uint16_t x, y, z; - uint64_t vectorZ[aN]; - - // Encode data into vector z - for (auto i = 0; i < aN; i++) { - x = (uint16_t) (aLocations.GetLocationX()[i] * (double) UINT16_MAX + .5); - y = (uint16_t) (aLocations.GetLocationY()[i] * (double) UINT16_MAX + .5); - if (aDimension != Dimension2D) { - z = (uint16_t) (aLocations.GetLocationZ()[i] * (double) UINT16_MAX + .5); - } else { - z = (uint16_t) 0.0; - } - vectorZ[i] = (SpreadBits(z) << 2) + (SpreadBits(y) << 1) + SpreadBits(x); - } - // Sort vector z - std::sort(vectorZ, vectorZ + aN, CompareUint64); - - // Decode data from vector z - for (auto i = 0; i < aN; i++) { - x = ReverseSpreadBits(vectorZ[i] >> 0); - y = ReverseSpreadBits(vectorZ[i] >> 1); - z = ReverseSpreadBits(vectorZ[i] >> 2); - aLocations.GetLocationX()[i] = (double) x / (double) UINT16_MAX; - aLocations.GetLocationY()[i] = (double) y / (double) UINT16_MAX; - if (aDimension == Dimension3D) { - aLocations.GetLocationZ()[i] = (double) z / (double) UINT16_MAX; - } - } -} - -template -uint64_t SyntheticGenerator::SpreadBits(uint64_t aInputByte) { - aInputByte &= 0x000000000000ffff; - // aInputByte = ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- fedc ba98 7654 3210 - aInputByte = (aInputByte ^ (aInputByte << 24)) & 0x000000ff000000ff; - // aInputByte = ---- ---- ---- ---- ---- ---- fedc ba98 ---- ---- ---- ---- ---- ---- 7654 3210 - aInputByte = (aInputByte ^ (aInputByte << 12)) & 0x000f000f000f000f; //000 7000f000f000f - // aInputByte = ---- ---- ---- fedc ---- ---- ---- ba98 ---- ---- ---- 7654 ---- ---- ---- 3210 - aInputByte = (aInputByte ^ (aInputByte << 6)) & 0x0303030303030303; //0 0001 0 0011 0 0011 0 0011 0 - // aInputByte = ---- --fe ---- --dc ---- --ba ---- --98 ---- --76 ---- --54 ---- --32 ---- --10 - aInputByte = (aInputByte ^ (aInputByte << 3)) & 0x1111111111111111; - // aInputByte = ---f ---e ---d ---c ---b ---a ---9 ---8 ---7 ---6 ---5 ---4 ---3 ---2 ---1 ---0 - return aInputByte; -} - -template -uint64_t SyntheticGenerator::ReverseSpreadBits(uint64_t aInputByte) { - - aInputByte &= 0x1111111111111111; - // aInputByte = ---f ---e ---d ---c ---b ---a ---9 ---8 ---7 ---6 ---5 ---4 ---3 ---2 ---1 ---0 - aInputByte = (aInputByte ^ (aInputByte >> 3)) & 0x0303030303030303; - // aInputByte = ---- --fe ---- --dc ---- --ba ---- --98 ---- --76 ---- --54 ---- --32 ---- --10 - aInputByte = (aInputByte ^ (aInputByte >> 6)) & 0x000f000f000f000f; - // aInputByte = ---- ---- ---- fedc ---- ---- ---- ba98 ---- ---- ---- 7654 ---- ---- ---- 3210 - aInputByte = (aInputByte ^ (aInputByte >> 12)) & 0x000000ff000000ff; - // aInputByte = ---- ---- ---- ---- ---- ---- fedc ba98 ---- ---- ---- ---- ---- ---- 7654 3210 - aInputByte = (aInputByte ^ (aInputByte >> 24)) & 0x000000000000ffff; - // aInputByte = ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- fedc ba98 7654 3210 - return aInputByte; - -} - -template -bool SyntheticGenerator::CompareUint64(const uint64_t &aFirstValue, const uint64_t &aSecondValue) { - return aFirstValue < aSecondValue; -} - template void SyntheticGenerator::ReleaseInstance() { if (mpInstance != nullptr) { diff --git a/src/data-loader/CMakeLists.txt b/src/data-loader/CMakeLists.txt new file mode 100644 index 00000000..1c5d2e4c --- /dev/null +++ b/src/data-loader/CMakeLists.txt @@ -0,0 +1,20 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @brief CMake configuration file for Data loader module +# @version 1.0.0 +# @author Mahmoud ElKarargy +# @date 2023-02-14 + +# Add subdirectories for concrete implementations +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/concrete) + +# Add source files to the parent scope +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/DataLoader.cpp + ${SOURCES} + PARENT_SCOPE + ) diff --git a/src/data-loader/DataLoader.cpp b/src/data-loader/DataLoader.cpp new file mode 100644 index 00000000..6fc7dffa --- /dev/null +++ b/src/data-loader/DataLoader.cpp @@ -0,0 +1,70 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file SyntheticGenerator.cpp + * @brief Implementation of the SyntheticGenerator class + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2023-02-14 +**/ + +#include + +using namespace std; + +using namespace exageostat::dataLoader; +using namespace exageostat::dataunits; +using namespace exageostat::common; + +template +std::unique_ptr> +DataLoader::CreateData(exageostat::configurations::Configurations &aConfigurations, + const exageostat::hardware::ExaGeoStatHardware &aHardware, + exageostat::kernels::Kernel &aKernel) { + + // create vectors that will be populated with read data. + vector measurements_vector; + vector x_locations; + vector y_locations; + vector z_locations; + + aKernel.SetPValue(aConfigurations.GetTimeSlot()); + int p = aKernel.GetVariablesNumber(); + + //Read the data out of the CSV file. + this->ReadData(aConfigurations, measurements_vector, x_locations, y_locations, z_locations, p); + + //create data object + auto data = std::make_unique>(aConfigurations.GetProblemSize() / p, + aConfigurations.GetDimension()); + + //Initialize the descriptors. + auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); + linear_algebra_solver->SetContext(aHardware.GetChameleonContext()); + linear_algebra_solver->InitiateDescriptors(aConfigurations, *data->GetDescriptorData(), p); + linear_algebra_solver->ExaGeoStatLaSetTile(EXAGEOSTAT_UPPER_LOWER, 0, 0, + data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_C).chameleon_desc); + //populate data object with read data + for (int i = 0; i < aConfigurations.GetProblemSize() / p; i++) { + data->GetLocations()->GetLocationX()[i] = x_locations[i]; + data->GetLocations()->GetLocationY()[i] = y_locations[i]; + if (aConfigurations.GetDimension() != Dimension2D) { + data->GetLocations()->GetLocationZ()[i] = z_locations[i]; + } + } + for (int i = 0; i < aConfigurations.GetProblemSize(); i++) { + ((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_Z).chameleon_desc->mat)[i] = measurements_vector[i]; + } + + results::Results::GetInstance()->SetGeneratedLocationsNumber(aConfigurations.GetProblemSize() / p); + results::Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); + results::Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); + + return data; +} diff --git a/src/data-loader/concrete/CMakeLists.txt b/src/data-loader/concrete/CMakeLists.txt new file mode 100644 index 00000000..6bc7013a --- /dev/null +++ b/src/data-loader/concrete/CMakeLists.txt @@ -0,0 +1,17 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @brief CMake configuration file for the data loader module +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2023-02-14 + +# Add source files to the parent scope +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/CSVLoader.cpp + ${SOURCES} + PARENT_SCOPE + ) \ No newline at end of file diff --git a/src/data-generators/concrete/CSVDataGenerator.cpp b/src/data-loader/concrete/CSVLoader.cpp similarity index 61% rename from src/data-generators/concrete/CSVDataGenerator.cpp rename to src/data-loader/concrete/CSVLoader.cpp index 8d14d5a1..32a79331 100644 --- a/src/data-generators/concrete/CSVDataGenerator.cpp +++ b/src/data-loader/concrete/CSVLoader.cpp @@ -14,80 +14,30 @@ #include -#include +#include using namespace std; -using namespace exageostat::generators::csv; using namespace exageostat::configurations; using namespace exageostat::hardware; using namespace exageostat::dataunits; using namespace exageostat::kernels; using namespace exageostat::common; using namespace exageostat::linearAlgebra; +using namespace exageostat::dataLoader::csv; template -CSVDataGenerator *CSVDataGenerator::GetInstance() { +CSVLoader *CSVLoader::GetInstance() { if (mpInstance == nullptr) { - mpInstance = new CSVDataGenerator(); + mpInstance = new CSVLoader(); } return mpInstance; } template -unique_ptr> -CSVDataGenerator::CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, - exageostat::kernels::Kernel &aKernel) { - - // create vectors that will be populated with read data. - vector measurements_vector; - vector x_locations; - vector y_locations; - vector z_locations; - - aKernel.SetPValue(aConfigurations.GetTimeSlot()); - int p = aKernel.GetVariablesNumber(); - - //Read the data out of the CSV file. - ReadData(aConfigurations, measurements_vector, x_locations, y_locations, z_locations, p); - - //create data object - auto data = std::make_unique>(aConfigurations.GetProblemSize() / p, - aConfigurations.GetDimension()); - - //Initialize the descriptors. - auto linear_algebra_solver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); - linear_algebra_solver->SetContext(aHardware.GetChameleonContext()); - linear_algebra_solver->InitiateDescriptors(aConfigurations, *data->GetDescriptorData(), p); - linear_algebra_solver->ExaGeoStatLaSetTile(EXAGEOSTAT_UPPER_LOWER, 0, 0, - data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_C).chameleon_desc); - //populate data object with read data - for (int i = 0; i < aConfigurations.GetProblemSize() / p; i++) { - data->GetLocations()->GetLocationX()[i] = x_locations[i]; - data->GetLocations()->GetLocationY()[i] = y_locations[i]; - if (aConfigurations.GetDimension() != Dimension2D) { - data->GetLocations()->GetLocationZ()[i] = z_locations[i]; - } - } - for (int i = 0; i < aConfigurations.GetProblemSize(); i++) { - ((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc->mat)[i] = measurements_vector[i]; - } - - results::Results::GetInstance()->SetGeneratedLocationsNumber(aConfigurations.GetProblemSize() / p); - results::Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); - results::Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); - - return data; -} - -template -void -CSVDataGenerator::ReadData(Configurations &aConfigurations, vector &aMeasurementsMatrix, vector &aXLocations, - vector &aYLocations, vector &aZLocations, const int &aP) { +void CSVLoader::ReadData(Configurations &aConfigurations, vector &aMeasurementsMatrix, vector &aXLocations, + vector &aYLocations, vector &aZLocations, const int &aP) { //Check if the user entered a valid path for the CSV file. if (aConfigurations.GetDataPath().empty()) { @@ -121,9 +71,9 @@ CSVDataGenerator::ReadData(Configurations &aConfigurations, vector &aMeasu aYLocations.push_back(stod(token)); } if (getline(iss, token, ',')) { - //If it's a 2D locations data, the last values of the lines should be the measurement values. - //If it's 3D locations data, the third value of the line should be the Z coordinate. - //If it's ST location data, the third value of the line should be the Time coordinate + //If its a 2D locations' data, the last values of the lines should be the measurement values. + //If its 3D locations' data, the third value of the line should be the Z coordinate. + //If its ST location data, the third value of the line should be the Time coordinate if (dimension == Dimension2D) { aMeasurementsMatrix.push_back(stod(token)); //if p == 2, the third and fourth values of each line are saved in the Measurements Matrix. @@ -193,10 +143,10 @@ CSVDataGenerator::ReadData(Configurations &aConfigurations, vector &aMeasu } template -void CSVDataGenerator::ReleaseInstance() { +void CSVLoader::ReleaseInstance() { if (mpInstance != nullptr) { mpInstance = nullptr; } } -template CSVDataGenerator *CSVDataGenerator::mpInstance = nullptr; +template CSVLoader *CSVLoader::mpInstance = nullptr; diff --git a/src/helpers/BasselFunction.cpp b/src/helpers/BasselFunction.cpp new file mode 100644 index 00000000..79540be8 --- /dev/null +++ b/src/helpers/BasselFunction.cpp @@ -0,0 +1,44 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file BasselFunction + * @brief This file contains the BasselFunction class which provides methods for computing derivatives of the modified Bessel function of the second kind. These functions are crucial in statistical and mathematical computations, especially in fields such as geostatistics and spatial analysis. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-01-24 +**/ + +extern "C" { +#include +} + +#include + +using namespace exageostat::helpers; + +template +T BasselFunction::CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue) { + if (aOrder == 0) { + return 0; + } else { + // Use a small step size to calculate the derivative numerically + const T step_size = 0.000000001; + return (gsl_sf_bessel_Knu(aOrder + step_size, aInputValue) - gsl_sf_bessel_Knu(aOrder, aInputValue)) / + step_size; + } +} + +template +T BasselFunction::CalculateSecondDerivativeBesselNu(const T &aOrder, const T &aInputValue) { + return (-0.5 * (CalculateSecondDerivativeBesselNuInput(aOrder - 1, aInputValue) + + CalculateSecondDerivativeBesselNuInput(aOrder + 1, aInputValue))); +} + +template +T BasselFunction::CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aInputValue) { + return (aOrder / aInputValue * gsl_sf_bessel_Knu(aOrder, aInputValue) - gsl_sf_bessel_Knu(aOrder + 1, aInputValue)); +} diff --git a/src/helpers/ByteHandler.cpp b/src/helpers/ByteHandler.cpp new file mode 100644 index 00000000..da38c2bb --- /dev/null +++ b/src/helpers/ByteHandler.cpp @@ -0,0 +1,52 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ByteHandler.hpp + * @brief Implementation of byte manipulation functions for ExaGeoStat. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-01-24 +**/ + +#include + +namespace exageostat::helpers { + + uint64_t SpreadBits(uint64_t aInputByte) { + + aInputByte &= 0x000000000000ffff; + // aInputByte = ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- fedc ba98 7654 3210 + aInputByte = (aInputByte ^ (aInputByte << 24)) & 0x000000ff000000ff; + // aInputByte = ---- ---- ---- ---- ---- ---- fedc ba98 ---- ---- ---- ---- ---- ---- 7654 3210 + aInputByte = (aInputByte ^ (aInputByte << 12)) & 0x000f000f000f000f; //000 7000f000f000f + // aInputByte = ---- ---- ---- fedc ---- ---- ---- ba98 ---- ---- ---- 7654 ---- ---- ---- 3210 + aInputByte = (aInputByte ^ (aInputByte << 6)) & 0x0303030303030303; //0 0001 0 0011 0 0011 0 0011 0 + // aInputByte = ---- --fe ---- --dc ---- --ba ---- --98 ---- --76 ---- --54 ---- --32 ---- --10 + aInputByte = (aInputByte ^ (aInputByte << 3)) & 0x1111111111111111; + // aInputByte = ---f ---e ---d ---c ---b ---a ---9 ---8 ---7 ---6 ---5 ---4 ---3 ---2 ---1 ---0 + return aInputByte; + } + + uint64_t ReverseSpreadBits(uint64_t aInputByte) { + + aInputByte &= 0x1111111111111111; + // aInputByte = ---f ---e ---d ---c ---b ---a ---9 ---8 ---7 ---6 ---5 ---4 ---3 ---2 ---1 ---0 + aInputByte = (aInputByte ^ (aInputByte >> 3)) & 0x0303030303030303; + // aInputByte = ---- --fe ---- --dc ---- --ba ---- --98 ---- --76 ---- --54 ---- --32 ---- --10 + aInputByte = (aInputByte ^ (aInputByte >> 6)) & 0x000f000f000f000f; + // aInputByte = ---- ---- ---- fedc ---- ---- ---- ba98 ---- ---- ---- 7654 ---- ---- ---- 3210 + aInputByte = (aInputByte ^ (aInputByte >> 12)) & 0x000000ff000000ff; + // aInputByte = ---- ---- ---- ---- ---- ---- fedc ba98 ---- ---- ---- ---- ---- ---- 7654 3210 + aInputByte = (aInputByte ^ (aInputByte >> 24)) & 0x000000000000ffff; + // aInputByte = ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- fedc ba98 7654 3210 + return aInputByte; + } + + bool CompareUint64(const uint64_t &aFirstValue, const uint64_t &aSecondValue) { + return aFirstValue < aSecondValue; + } +} \ No newline at end of file diff --git a/src/helpers/CMakeLists.txt b/src/helpers/CMakeLists.txt index 5c01b62a..af5f9ae7 100644 --- a/src/helpers/CMakeLists.txt +++ b/src/helpers/CMakeLists.txt @@ -13,6 +13,8 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/DiskWriter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DistanceCalculationHelpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CommunicatorMPI.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ByteHandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BasselFunction.cpp ${SOURCES} PARENT_SCOPE ) \ No newline at end of file diff --git a/src/helpers/DiskWriter.cpp b/src/helpers/DiskWriter.cpp index b168c634..99a3668f 100644 --- a/src/helpers/DiskWriter.cpp +++ b/src/helpers/DiskWriter.cpp @@ -12,11 +12,10 @@ * @date 2023-06-08 **/ -#include #include -#include #include + using namespace std; using namespace exageostat::helpers; @@ -111,4 +110,4 @@ void DiskWriter::WriteVectorsToDisk(const T &aMatrixPointer, const int &aProb } } p_file_synthetic.close(); -} +} \ No newline at end of file diff --git a/src/kernels/Kernel.cpp b/src/kernels/Kernel.cpp index 953cbb7c..193e3e04 100644 --- a/src/kernels/Kernel.cpp +++ b/src/kernels/Kernel.cpp @@ -12,40 +12,12 @@ * @date 2023-04-12 **/ -extern "C" { -#include -} - #include using namespace std; -using namespace exageostat::dataunits; using namespace exageostat::kernels; -template -T Kernel::CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue) { - if (aOrder == 0) { - return 0; - } else { - // Use a small step size to calculate the derivative numerically - const T step_size = 0.000000001; - return (gsl_sf_bessel_Knu(aOrder + step_size, aInputValue) - gsl_sf_bessel_Knu(aOrder, aInputValue)) / - step_size; - } -} - -template -T Kernel::CalculateSecondDerivativeBesselNu(const T &aOrder, const T &aInputValue) { - return (-0.5 * (CalculateSecondDerivativeBesselNuInput(aOrder - 1, aInputValue) + - CalculateSecondDerivativeBesselNuInput(aOrder + 1, aInputValue))); -} - -template -T Kernel::CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aInputValue) { - return (aOrder / aInputValue * gsl_sf_bessel_Knu(aOrder, aInputValue) - gsl_sf_bessel_Knu(aOrder + 1, aInputValue)); -} - template int Kernel::GetVariablesNumber() const { return this->mVariablesNumber; diff --git a/src/kernels/concrete/BivariateMaternFlexible.cpp b/src/kernels/concrete/BivariateMaternFlexible.cpp index 39a67c18..d70c75aa 100644 --- a/src/kernels/concrete/BivariateMaternFlexible.cpp +++ b/src/kernels/concrete/BivariateMaternFlexible.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/BivariateMaternParsimonious.cpp b/src/kernels/concrete/BivariateMaternParsimonious.cpp index 223dbd3e..f222fe46 100644 --- a/src/kernels/concrete/BivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/BivariateMaternParsimonious.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp index 08e65389..8209086f 100644 --- a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/TrivariateMaternParsimonious.cpp b/src/kernels/concrete/TrivariateMaternParsimonious.cpp index d5973678..6f833b6e 100644 --- a/src/kernels/concrete/TrivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/TrivariateMaternParsimonious.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateExpNonGaussian.cpp b/src/kernels/concrete/UnivariateExpNonGaussian.cpp index 2007d78d..6be4bf66 100644 --- a/src/kernels/concrete/UnivariateExpNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateExpNonGaussian.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternDbeta.cpp b/src/kernels/concrete/UnivariateMaternDbeta.cpp index c0253e94..caa2041a 100644 --- a/src/kernels/concrete/UnivariateMaternDbeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDbeta.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp index dec11267..140c5659 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp index e202b96d..ea9c173a 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp @@ -13,7 +13,6 @@ **/ #include -#include using namespace exageostat::kernels; using namespace exageostat::dataunits; @@ -98,7 +97,7 @@ UnivariateMaternDdbetaNu::GenerateCovarianceMatrix(T *apMatrixA, const int &a aLocalTheta[2] + 1, expr)) + pow(expr, aLocalTheta[2]) * - this->CalculateSecondDerivativeBesselNuInput( + BasselFunction::CalculateSecondDerivativeBesselNuInput( aLocalTheta[2], expr))); apMatrixA[i + j * aRowsNumber] = (-1 / aLocalTheta[1] * (con * pow(expr, aLocalTheta[2]) * gsl_sf_bessel_Knu(aLocalTheta[2], expr)) - diff --git a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp index 7dec948b..a54a1ce4 100644 --- a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; @@ -72,22 +72,20 @@ UnivariateMaternDdnuNu::GenerateCovarianceMatrix(T *apMatrixA, const int &aRo (pow(expr, aLocalTheta[2]) * log(expr) * gsl_sf_bessel_Knu(aLocalTheta[2], expr) + pow(expr, aLocalTheta[2]) * - this->CalculateDerivativeBesselNu(aLocalTheta[2], + BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr))); nu_expr_dprime = (1 - aLocalTheta[2]) * 1 / pow(2, aLocalTheta[2]) * 1 / tgamma(aLocalTheta[2]) * - pow(expr, aLocalTheta[2]) * this->CalculateDerivativeBesselNu(aLocalTheta[2], expr) + + pow(expr, aLocalTheta[2]) * BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr) + pow(2, 1 - aLocalTheta[2]) * (-1 / tgamma(aLocalTheta[2]) * gsl_sf_psi(aLocalTheta[2]) * pow(expr, aLocalTheta[2]) * - this->CalculateDerivativeBesselNu(aLocalTheta[2], expr) + 1 / tgamma(aLocalTheta[2]) * + BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr) + 1 / tgamma(aLocalTheta[2]) * (pow(expr, aLocalTheta[2]) * log(expr) * - this->CalculateDerivativeBesselNu( + BasselFunction::CalculateDerivativeBesselNu( aLocalTheta[2], expr) + pow(expr, aLocalTheta[2]) * - this->CalculateSecondDerivativeBesselNu( - aLocalTheta[2], - expr))); + BasselFunction::CalculateSecondDerivativeBesselNu(aLocalTheta[2],expr))); apMatrixA[i + j * aRowsNumber] = (-0.5 * con * pow(expr, aLocalTheta[2]) * gsl_sf_bessel_Knu(aLocalTheta[2], expr) + (1 - aLocalTheta[2]) / 2 * nu_expr - diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp index 79c15bc4..dc94d289 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp index 19623e65..f6e62a31 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternDnu.cpp b/src/kernels/concrete/UnivariateMaternDnu.cpp index c62e47c1..e357826c 100644 --- a/src/kernels/concrete/UnivariateMaternDnu.cpp +++ b/src/kernels/concrete/UnivariateMaternDnu.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; @@ -68,7 +68,7 @@ void UnivariateMaternDnu::GenerateCovarianceMatrix(T *apMatrixA, const int &a (pow(expr, aLocalTheta[2]) * log(expr) * gsl_sf_bessel_Knu(aLocalTheta[2], expr) + pow(expr, aLocalTheta[2]) * - Kernel::CalculateDerivativeBesselNu(aLocalTheta[2], expr))); + BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr))); apMatrixA[i + j * aRowsNumber] = sigma_square * nu_expr; } j0++; diff --git a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp index 2973c2af..d6d55635 100644 --- a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp @@ -15,7 +15,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp index 6d871568..d18410af 100644 --- a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp index 7d899d7b..a4a1d7f6 100644 --- a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternStationary.cpp b/src/kernels/concrete/UnivariateMaternStationary.cpp index 93de1667..9f576a8b 100644 --- a/src/kernels/concrete/UnivariateMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternStationary.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp index 19a99bce..b4631421 100644 --- a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp @@ -17,7 +17,7 @@ #include #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp index 3ff0971f..a86c4717 100644 --- a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include using namespace std; @@ -29,6 +31,7 @@ using namespace exageostat::dataunits; using namespace exageostat::common; using namespace exageostat::configurations; using namespace exageostat::kernels; +using namespace exageostat::helpers; void TEST_SPREAD_REVERSED_BITS() { @@ -41,7 +44,7 @@ void TEST_SPREAD_REVERSED_BITS() { { uint16_t randomByte = INT16_MAX; REQUIRE(randomByte == 0x7FFF); - uint64_t returnedByte = SyntheticGenerator::SpreadBits(randomByte); + uint64_t returnedByte = SpreadBits(randomByte); // This because 7FFF will first be 16 hex = 64 bits // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 7FFF // 7FFF to bits is 0111111111111111 @@ -51,7 +54,7 @@ void TEST_SPREAD_REVERSED_BITS() { }SECTION("Reverse Spread Bytes") { uint64_t randomByte = 0x0111111111111111; - uint16_t returnedByte = SyntheticGenerator::ReverseSpreadBits(randomByte); + uint16_t returnedByte = ReverseSpreadBits(randomByte); REQUIRE(returnedByte == 0x7FFF); }SECTION("Spread & reverse 3D") { @@ -61,7 +64,7 @@ void TEST_SPREAD_REVERSED_BITS() { uint16_t z = INT16_MAX; uint64_t vectorZ; - vectorZ = (SyntheticGenerator::SpreadBits(z) << 2); + vectorZ = (SpreadBits(z) << 2); // vector Z will be // ---- ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 // After shifting by 2 @@ -70,7 +73,7 @@ void TEST_SPREAD_REVERSED_BITS() { REQUIRE(vectorZ == 0x0444444444444444); // Do the same for Y - vectorZ += (SyntheticGenerator::SpreadBits(y) << 1); + vectorZ += (SpreadBits(y) << 1); // If vector Z was empty it will be // ---- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- // But sine vectorZ is already contains Z. then by adding both we get @@ -79,16 +82,16 @@ void TEST_SPREAD_REVERSED_BITS() { REQUIRE(vectorZ == 0x0666666666666666); // Lastly, Adding X - vectorZ += SyntheticGenerator::SpreadBits(x); + vectorZ += SpreadBits(x); // Adding X without shifting will result in // ---- -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 // Since 0111 is equal to 7 in hex then expected to be 0x0777777777777777 REQUIRE(vectorZ == 0x0777777777777777); // Spreading is Done, Now reversing. - uint16_t reversed_x = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 0); - uint16_t reversed_y = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 1); - uint16_t reversed_z = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 2); + uint16_t reversed_x = ReverseSpreadBits(vectorZ >> 0); + uint16_t reversed_y = ReverseSpreadBits(vectorZ >> 1); + uint16_t reversed_z = ReverseSpreadBits(vectorZ >> 2); // What we reversed is what we send. REQUIRE(reversed_x == INT16_MAX); @@ -107,13 +110,13 @@ void TEST_SPREAD_REVERSED_BITS() { uint16_t z_random = 22222; //Spreading - vectorZ = (SyntheticGenerator::SpreadBits(z_random) << 2) + - (SyntheticGenerator::SpreadBits(y_random) << 1) + - SyntheticGenerator::SpreadBits(x_random); + vectorZ = (SpreadBits(z_random) << 2) + + (SpreadBits(y_random) << 1) + + SpreadBits(x_random); // Spreading is Done, Now reversing. - uint16_t reversed_x_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 0); - uint16_t reversed_y_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 1); - uint16_t reversed_z_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 2); + uint16_t reversed_x_random = ReverseSpreadBits(vectorZ >> 0); + uint16_t reversed_y_random = ReverseSpreadBits(vectorZ >> 1); + uint16_t reversed_z_random = ReverseSpreadBits(vectorZ >> 2); REQUIRE(x_random == reversed_x_random); REQUIRE(y_random == reversed_y_random); @@ -128,28 +131,28 @@ void TEST_SPREAD_REVERSED_BITS() { uint16_t z = 0; uint64_t vectorZ; - vectorZ = (SyntheticGenerator::SpreadBits(z) << 2); + vectorZ = (SpreadBits(z) << 2); // vector Z will be zeros REQUIRE(vectorZ == 0x0000000000000000); // Do the same for Y - vectorZ += (SyntheticGenerator::SpreadBits(y) << 1); + vectorZ += (SpreadBits(y) << 1); // vector Z after shift by one will be // ---- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- // Since 0010 is equal to 2 in hex then expected to be 0x022222222222222 REQUIRE(vectorZ == 0x0222222222222222); // Lastly, Adding X - vectorZ += SyntheticGenerator::SpreadBits(x); + vectorZ += SpreadBits(x); // Adding X without shifting will result in // ---- --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 // Since 0011 is equal to 3 in hex then expected to be 0x0333333333333333 REQUIRE(vectorZ == 0x0333333333333333); // Spreading is Done, Now reversing. - uint16_t reversed_x = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 0); - uint16_t reversed_y = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 1); - uint16_t reversed_z = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 2); + uint16_t reversed_x = ReverseSpreadBits(vectorZ >> 0); + uint16_t reversed_y = ReverseSpreadBits(vectorZ >> 1); + uint16_t reversed_z = ReverseSpreadBits(vectorZ >> 2); // What we reversed is what we send. REQUIRE(reversed_x == INT16_MAX); @@ -168,13 +171,13 @@ void TEST_SPREAD_REVERSED_BITS() { uint16_t z_random = 0; //Spreading - vectorZ = (SyntheticGenerator::SpreadBits(z_random) << 2) + - (SyntheticGenerator::SpreadBits(y_random) << 1) + - SyntheticGenerator::SpreadBits(x_random); + vectorZ = (SpreadBits(z_random) << 2) + + (SpreadBits(y_random) << 1) + + SpreadBits(x_random); // Spreading is Done, Now reversing. - uint16_t reversed_x_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 0); - uint16_t reversed_y_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 1); - uint16_t reversed_z_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 2); + uint16_t reversed_x_random = ReverseSpreadBits(vectorZ >> 0); + uint16_t reversed_y_random = ReverseSpreadBits(vectorZ >> 1); + uint16_t reversed_z_random = ReverseSpreadBits(vectorZ >> 2); REQUIRE(x_random == reversed_x_random); REQUIRE(y_random == reversed_y_random); @@ -268,7 +271,7 @@ void TEST_HELPERS_FUNCTIONS() { { double lowerRange = -0.4; double higherRange = 0.4; - double uniformed_num = SyntheticGenerator::UniformDistribution(lowerRange, higherRange); + double uniformed_num = LocationGenerator::UniformDistribution(lowerRange, higherRange); REQUIRE(uniformed_num > lowerRange); REQUIRE(uniformed_num < 1); } @@ -276,9 +279,9 @@ void TEST_HELPERS_FUNCTIONS() { SECTION("Compare Uint32") { uint32_t num1 = 16; - REQUIRE(SyntheticGenerator::CompareUint64(num1, num1) == false); - REQUIRE(SyntheticGenerator::CompareUint64(num1, num1 + num1) == true); - REQUIRE(SyntheticGenerator::CompareUint64(num1 + num1, num1) == false); + REQUIRE(CompareUint64(num1, num1) == false); + REQUIRE(CompareUint64(num1, num1 + num1) == true); + REQUIRE(CompareUint64(num1 + num1, num1) == false); SyntheticGenerator::ReleaseInstance(); } } diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index e4bcf929..bb3f23a3 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -135,7 +135,6 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp } arguments_vector.push_back("--cores=" + to_string(cpu_size_distribution(gen))); - // TODO: Till fixing cuda error with multiple devices. #ifdef USE_CUDA int nDevices; cudaGetDeviceCount(&nDevices); From 836ccbc411dad920c159682e758ccd4dec03973a Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 11 Feb 2024 23:08:07 +0200 Subject: [PATCH 22/82] updates --- .gitignore | 3 +- CHANGELOG.md | 7 +- DESCRIPTION | 7 +- cleanup | 7 +- cmake/CreateSymbolTable.R | 62 ++++++ cmake/FindR.cmake | 77 ++++--- configure | 76 ++++++- examples/configurations/CMakeLists.txt | 6 +- .../RunningWithDifferentConfigurations.cpp | 73 +++++++ ...tionModule.cpp => SetupConfigurations.cpp} | 12 +- .../Rcpp-adapters/FunctionsAdapter.hpp | 54 +++-- inst/include/api/ExaGeoStat.hpp | 8 +- inst/include/common/Definitions.hpp | 9 + .../include/configurations/Configurations.hpp | 10 +- .../include/data-generators/DataGenerator.hpp | 8 +- .../data-generators/LocationGenerator.hpp | 72 +++++++ .../concrete/CSVDataGenerator.hpp | 97 --------- .../concrete/SyntheticGenerator.hpp | 60 +----- inst/include/data-loader/DataLoader.hpp | 66 ++++++ .../data-loader/concrete/CSVLoader.hpp | 84 ++++++++ inst/include/data-units/DescriptorData.hpp | 2 +- inst/include/data-units/ExaGeoStatData.hpp | 163 ++++++++------- inst/include/data-units/Locations.hpp | 4 +- inst/include/hardware/ExaGeoStatHardware.hpp | 50 ++--- inst/include/helpers/BasselFunction.hpp | 69 +++++++ inst/include/helpers/ByteHandler.hpp | 48 +++++ inst/include/helpers/DiskWriter.hpp | 2 +- inst/include/kernels/Kernel.hpp | 28 +-- .../LinearAlgebraMethods.hpp | 18 +- .../chameleon/ChameleonImplementation.hpp | 2 +- .../hicma/tlr/HicmaImplementation.hpp | 4 +- inst/include/prediction/Prediction.hpp | 31 +-- .../PredictionAuxiliaryFunctions.hpp | 10 - inst/include/prediction/PredictionHelpers.hpp | 2 +- inst/include/results/Results.hpp | 42 ++++ inst/include/utilities/EnumStringParser.hpp | 6 +- src/CMakeLists.txt | 23 ++- src/Rcpp-adapters/FunctionsAdapter.cpp | 66 +++--- src/Rcpp-adapters/RcppModules.cpp | 18 +- src/api/ExaGeoStat.cpp | 7 +- src/configurations/Configurations.cpp | 16 +- src/data-generators/CMakeLists.txt | 3 +- src/data-generators/DataGenerator.cpp | 26 ++- src/data-generators/LocationGenerator.cpp | 122 +++++++++++ src/data-generators/concrete/CMakeLists.txt | 2 - .../concrete/SyntheticGenerator.cpp | 148 +------------- src/data-loader/CMakeLists.txt | 20 ++ src/data-loader/DataLoader.cpp | 70 +++++++ src/data-loader/concrete/CMakeLists.txt | 17 ++ .../concrete/CSVLoader.cpp} | 74 ++----- src/hardware/ExaGeoStatHardware.cpp | 75 ++++--- src/helpers/BasselFunction.cpp | 44 ++++ src/helpers/ByteHandler.cpp | 52 +++++ src/helpers/CMakeLists.txt | 2 + src/helpers/DiskWriter.cpp | 5 +- src/kernels/Kernel.cpp | 28 --- .../concrete/BivariateMaternFlexible.cpp | 2 +- .../concrete/BivariateMaternParsimonious.cpp | 2 +- .../BivariateSpacetimeMaternStationary.cpp | 2 +- .../concrete/TrivariateMaternParsimonious.cpp | 2 +- .../concrete/UnivariateExpNonGaussian.cpp | 2 +- .../concrete/UnivariateMaternDbeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaBeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaNu.cpp | 3 +- .../concrete/UnivariateMaternDdnuNu.cpp | 14 +- .../UnivariateMaternDdsigmaSquareBeta.cpp | 2 +- .../UnivariateMaternDdsigmaSquareNu.cpp | 2 +- src/kernels/concrete/UnivariateMaternDnu.cpp | 4 +- .../concrete/UnivariateMaternDsigmaSquare.cpp | 2 +- .../concrete/UnivariateMaternNonGaussian.cpp | 2 +- .../UnivariateMaternNuggetsStationary.cpp | 2 +- .../concrete/UnivariateMaternStationary.cpp | 2 +- .../UnivariateSpacetimeMaternStationary.cpp | 2 +- src/prediction/Prediction.cpp | 7 +- src/results/Results.cpp | 35 +++- tests/R-tests/TestExaGeoStat.R | 53 +++++ tests/cpp-tests/CMakeLists.txt | 19 +- tests/cpp-tests/Rcpp-adapters/CMakeLists.txt | 16 ++ .../Rcpp-adapters/TestAllRFunctions.cpp | 43 ++++ .../configurations/TestConfigurations.cpp | 71 ++++++- .../concrete/TestSyntheticGenerator.cpp | 63 +++--- tests/cpp-tests/data-units/CMakeLists.txt | 14 ++ .../data-units/TestDescriptorData.cpp | 64 ++++++ tests/cpp-tests/hardware/CMakeLists.txt | 14 ++ .../hardware/TestExaGeoStatHardware.cpp | 111 ++++++++++ tests/cpp-tests/helpers/CMakeLists.txt | 8 +- tests/cpp-tests/helpers/TestDiskWriter.cpp | 80 ++++++++ .../TestDistanceCalculationHelpers.cpp | 144 +++++++++++++ .../concrete/TestBivariateMaternFlexible.cpp | 2 +- .../TestBivariateMaternParsimonious.cpp | 2 +- ...TestBivariateSpacetimeMaternStationary.cpp | 2 +- .../TestTrivariateMaternParsimonious.cpp | 2 +- .../TestUnivariateMaternNonGaussian.cpp | 2 +- .../TestUnivariateMaternNuggetsStationary.cpp | 2 +- .../TestUnivariateMaternStationary.cpp | 2 +- ...estUnivariateSpacetimeMaternStationary.cpp | 2 +- tests/cpp-tests/prediction/CMakeLists.txt | 18 ++ tests/cpp-tests/prediction/TestPrediction.cpp | 189 ++++++++++++++++++ .../TestPredictionHelpers.cpp | 2 +- tests/cpp-tests/results/CMakeLists.txt | 14 ++ tests/cpp-tests/results/TestResults.cpp | 59 ++++++ tests/heavy-tests/HeavyTests.cpp | 9 +- 102 files changed, 2352 insertions(+), 843 deletions(-) create mode 100644 cmake/CreateSymbolTable.R create mode 100644 examples/configurations/RunningWithDifferentConfigurations.cpp rename examples/configurations/{ConfigurationModule.cpp => SetupConfigurations.cpp} (88%) create mode 100644 inst/include/data-generators/LocationGenerator.hpp delete mode 100644 inst/include/data-generators/concrete/CSVDataGenerator.hpp create mode 100644 inst/include/data-loader/DataLoader.hpp create mode 100644 inst/include/data-loader/concrete/CSVLoader.hpp create mode 100644 inst/include/helpers/BasselFunction.hpp create mode 100644 inst/include/helpers/ByteHandler.hpp create mode 100644 src/data-generators/LocationGenerator.cpp create mode 100644 src/data-loader/CMakeLists.txt create mode 100644 src/data-loader/DataLoader.cpp create mode 100644 src/data-loader/concrete/CMakeLists.txt rename src/{data-generators/concrete/CSVDataGenerator.cpp => data-loader/concrete/CSVLoader.cpp} (60%) create mode 100644 src/helpers/BasselFunction.cpp create mode 100644 src/helpers/ByteHandler.cpp create mode 100644 tests/R-tests/TestExaGeoStat.R create mode 100644 tests/cpp-tests/Rcpp-adapters/CMakeLists.txt create mode 100644 tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp create mode 100644 tests/cpp-tests/data-units/CMakeLists.txt create mode 100644 tests/cpp-tests/data-units/TestDescriptorData.cpp create mode 100644 tests/cpp-tests/hardware/CMakeLists.txt create mode 100644 tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp create mode 100644 tests/cpp-tests/helpers/TestDiskWriter.cpp create mode 100644 tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp create mode 100644 tests/cpp-tests/prediction/CMakeLists.txt create mode 100644 tests/cpp-tests/prediction/TestPrediction.cpp rename tests/cpp-tests/{helpers => prediction}/TestPredictionHelpers.cpp (99%) create mode 100644 tests/cpp-tests/results/CMakeLists.txt create mode 100644 tests/cpp-tests/results/TestResults.cpp diff --git a/.gitignore b/.gitignore index 4ab21a78..da74638d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # synthetic data for testing tests/cpp-tests/data-generators/concrete/synthetic_ds +synthetic_ds/ # intallation directory installdir/ @@ -11,6 +12,7 @@ installdir/ .Rhistory .RData .RDataTmp +src/symbols.rds # tar.gz files *.tar.gz @@ -672,7 +674,6 @@ bh_unicode_properties.cache GitHub.sublime-settings # Sesimic Toolbox Results Specifics -results/ *.trace *.segy *.sgy diff --git a/CHANGELOG.md b/CHANGELOG.md index 15c11f29..41fc89f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Implemented a new changelog. - Introduced a benchmarking script. -- .gitignore file +- .gitignore file. +- More examples. +- Add tests for all src files. ### Fixed - Resolved issues with MPI installation. @@ -24,6 +26,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Adjusted CMake variables. - Revised the process of finding BLASPP and Catch2 libraries. - Updated doxygen documentation. +- Split the synthetic generator functions into BitHelper class and Locations generator class. +- Created a Bassel Function helper for kernels. +- Cleaned the code base for better readability. ### Removed - Eliminated non-stationary kernel support. diff --git a/DESCRIPTION b/DESCRIPTION index 26734fd3..f69330e0 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -4,13 +4,13 @@ Title: R Package Demonstrates the R/C++ Language Interface for Exascale GeoStati Version: 1.1.0 Date: 2024-01-14 Author: Mahmoud ElKarargy [aut, cph], Sameh Abdulah [cre, cph], KAUST King Abdullah University of Science and Technology [fnd, cph], Brightskies [cph] -Maintainer: Sameh Abdulah , Mahmoud ElKarargy +Maintainer: Sameh Abdulah Description: An R-wrapper for ExaGeoStatCPP: a parallel high performance unified framework for geostatistics on manycore systems. Its abbreviation stands for Exascale Geostatistics. The framework aims at optimizing the likelihood function for a given spatial data to provide an efficient way to predict missing observations. The framework targets many-core systems: clusters of CPUs and GPUs. License: GPL (>= 3) Imports: assertthat (>= 0.2.1), MASS, methods, Rcpp (>= 1.0.9) Depends: R (>= 3.5.0), assertthat (>= 0.2.1), MASS RoxygenNote: 7.2.3 -SystemRequirements: CMake (>=3.20), C++ (>= 11), nlopt (>= 2.4.2 http://ab-initio.mit.edu), lapacke (https://github.com/xianyi/OpenBLAS/releases), blas (https://github.com/xianyi/OpenBLAS/releases), hwloc (>=1.11.5 https://www.open-mpi.org), gsl (>= 2.4 https://ftp.gnu.org) +SystemRequirements: C++ (>= 11), lapacke (https://github.com/xianyi/OpenBLAS/releases), blas (https://github.com/xianyi/OpenBLAS/releases) NeedsCompilation: yes OS_type: unix Authors@R: c( @@ -20,4 +20,5 @@ Authors@R: c( person("Brightskies", role=c("cph")) ) URL: https://www.github.com/ecrc/ExaGeoStatCPP -Encoding: UTF-8 +BugReports: https://github.com/ecrc/ExaGeoStatCPP/issues +Encoding: UTF-8 \ No newline at end of file diff --git a/cleanup b/cleanup index 0a1885df..9d930071 100755 --- a/cleanup +++ b/cleanup @@ -9,7 +9,7 @@ # @version 1.1.0 # @author David Helmy # @author Mahmoud Elkarargy -# @date 2024-01-15 +# @date 2024-02-09 rm -rf ./src/*.so* rm -rf ./src/*.d @@ -21,7 +21,12 @@ rm -rf ./.git/ rm -rf ./config.sh rm -rf ./clean_build.sh rm -rf ./..Rcheck/ +rm -rf ./benchmarks/ +rm -rf ./doxygen_config +rm -rf ./prerequisites/ rm -rf ./scripts/ rm -rf ./installdir/ mv ./LICENSE.md ./LICENSE +mv ./cmake/ ./src/cmake/ +mv ./CMakeLists.txt ./src/CMakeLists_inv.txt \ No newline at end of file diff --git a/cmake/CreateSymbolTable.R b/cmake/CreateSymbolTable.R new file mode 100644 index 00000000..a8dd047a --- /dev/null +++ b/cmake/CreateSymbolTable.R @@ -0,0 +1,62 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CreateSymbolTable.R +# @version 1.1.0 +# @author David Helmy +# @date 2024-02-09 + +read_symbols_from_object_file <- function(f){ + nm <- Sys.getenv("UserNM") + if(!nzchar(nm)) { + ## reasonable to assume nm is on the path + nm <- Sys.which("nm") + if(nzchar(nm)) nm <- shQuote(nm) + } + if(!nzchar(nm)) { + warning("this requires 'nm' to be on the PATH") + return() + } + if(!(file.size(f))) return() + s <- strsplit(system(sprintf("%s -Pg %s", nm, shQuote(f)), + intern = TRUE), + " +") + ## Cannot simply rbind() this because elements may have 2-4 entries. + n <- length(s) + tab <- matrix("", nrow = n, ncol = 4L) + colnames(tab) <- c("name", "type", "value", "size") + ## Compute desired i and j positions in tab. + i <- rep.int(seq_len(n), lengths(s)) + j <- unlist(lapply(s, seq_along)) + tab[n * (j - 1L) + i] <- unlist(s) + tab +} +list_files_with_extension <- function(directory_path, file_extension) { + # Get a list of all files in the directory and its subdirectories + all_files <- list.files(directory_path, recursive = TRUE, full.names = TRUE) + + # Filter files based on the provided file extension + selected_files <- all_files[grep(paste0("\\.", file_extension, "$"), all_files, ignore.case = TRUE)] + + return(selected_files) +} + +# Define the arguments +args <- commandArgs(trailingOnly = TRUE) + +if (length(args) != 2) { + cat("\n\n\n\n") + stop("Please provide correct arguments") +} + +directory_path <- (args[1]) +file_path <- (args[2]) + +file_extension <- "o" + + +objects <- list_files_with_extension(directory_path, file_extension) +tables <- lapply(objects, read_symbols_from_object_file) +names(tables) <- objects +saveRDS(tables, file = file_path, version = 2) \ No newline at end of file diff --git a/cmake/FindR.cmake b/cmake/FindR.cmake index c0cde2c9..a341c146 100644 --- a/cmake/FindR.cmake +++ b/cmake/FindR.cmake @@ -25,7 +25,6 @@ # R_LIB_PATH ... if set, the Rcpp libraries are exclusively searched # under this path - if (DEFINED ENV{R_HOME}) set(R_ROOT_PATH "$ENV{R_HOME}") @@ -67,18 +66,17 @@ if (R_ROOT_PATH) if (APPLE) find_library( - R_LIB - REQUIRED + R_DYN_LIB NAMES "libR.dylib" PATHS ${R_ROOT_PATH} PATH_SUFFIXES "lib" "lib64" "bin" NO_DEFAULT_PATH ) + else () #find libs find_library( - R_LIB - REQUIRED + R_DYN_LIB NAMES "libR.so" PATHS ${R_ROOT_PATH} PATH_SUFFIXES "lib" "lib64" "bin" @@ -87,6 +85,15 @@ if (R_ROOT_PATH) endif () + if (R_DYN_LIB MATCHES R_DYN_LIB-NOTFOUND) + set(R_DYN_LIB "") + message("R is built with no dynamic library support") + endif () + + set(R_LIB + ${R_DYN_LIB} + ) + else () error("R is not installed ") endif (R_ROOT_PATH) @@ -105,7 +112,6 @@ if (R_INCLUDE_PATH) endif () if (RCPP_LIB_PATH) - #find libs find_library( RCPP_LIB @@ -128,32 +134,9 @@ if (RCPP_LIB_PATH) else () - - #find libs - find_library( - RCPP_LIB - REQUIRED - NAMES "Rcpp.so" - PATHS ${LIB_INSTALL_DIR} - ) - - #find includes - find_path( - RCPP_INCLUDE_DIRS - REQUIRED - NAMES "Rcpp.h" - PATHS ${INCLUDE_INSTALL_DIR} - ) - + message("Rcpp is not installed ...") endif (RCPP_LIB_PATH) - -set(R_LIBRARIES - ${R_LIBRARIES} - ${R_LIB} - ) - - set(R_INCLUDE ${R_INCLUDE} ${R_INCLUDE_DIRS} @@ -161,15 +144,29 @@ set(R_INCLUDE ) add_library(R INTERFACE IMPORTED) -set_target_properties(R - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${R_INCLUDE}" - INTERFACE_LINK_LIBRARIES "${R_LIBRARIES}" - IMPORTED_LOCATION ${RCPP_LIB} - ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(R DEFAULT_MSG - R_INCLUDE R_LIBRARIES) +if (R_LIB) + set_target_properties(R + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${R_INCLUDE}" + INTERFACE_LINK_LIBRARIES "${R_LIB}" + IMPORTED_LOCATION ${RCPP_LIB} + ) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(R DEFAULT_MSG + R_INCLUDE R_LIB) + + include_directories(${R_INCLUDE}) + mark_as_advanced(R_INCLUDE R_INCLUDE_DIRS RCPP_INCLUDE_DIRS R_LIB RCPP_LIB) -include_directories(${R_INCLUDE}) -mark_as_advanced(R_INCLUDE R_INCLUDE_DIRS RCPP_INCLUDE_DIRS R_LIBRARIES R_LIB RCPP_LIB) +else () + set_target_properties(R + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${R_INCLUDE}" + IMPORTED_LOCATION ${RCPP_LIB} + ) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(R DEFAULT_MSG + R_INCLUDE) + + include_directories(${R_INCLUDE}) + mark_as_advanced(R_INCLUDE R_INCLUDE_DIRS RCPP_INCLUDE_DIRS RCPP_LIB) +endif () \ No newline at end of file diff --git a/configure b/configure index 7abb25de..bc64f0c6 100755 --- a/configure +++ b/configure @@ -16,6 +16,35 @@ YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' +INSTALL_PREFIX=$PWD/installdir/_deps + +# Function to install CMake from source +install_cmake() { + echo "CMake not found. Installing CMake from source..." + + # Create a temporary directory for building CMake + temp_dir=$(mktemp -d) + cd "$temp_dir" || exit 1 + + # Download CMake source code + wget https://github.com/Kitware/CMake/releases/download/v3.28.1/cmake-3.28.1.tar.gz + + # Extract the source code + tar -xzvf cmake-3.28.1.tar.gz + + # Enter the extracted directory + cd cmake-3.28.1 || exit 1 + + # Configure, build, and install CMake to the specified location + ./bootstrap --prefix="$INSTALL_PREFIX" --parallel=2 -- -DCMAKE_USE_OPENSSL=OFF + make -j 2 + sudo make install + + # Clean up + cd "$temp_dir" || exit 1 + rm -rf "$temp_dir" +} + # shellcheck disable=SC2164 cd "$(dirname "$0")" # Get the operating system type using uname @@ -27,7 +56,7 @@ else ABSOLUTE_PATH=$(dirname "$(realpath "$0")") fi -INSTALL_PREFIX=$PWD/installdir/_deps + BUILDING_TESTS="OFF" BUILDING_HEAVY_TESTS="OFF" BUILDING_EXAMPLES="OFF" @@ -37,11 +66,23 @@ USE_CUDA="OFF" USE_MPI="OFF" BLAS_VENDOR="" PACKAGE="OFF" +SHOW_WARNINGS="OFF" COMPILE_FLAGS="-Wl,--no-as-needed -w -fpic" DEVELOPER_WARNINGS="-Wno-dev" +for arg in "$@" +do + case $arg in + --use-mkl) + echo "${GREEN}MKL as a BLA vendor${NC}" + BLAS_VENDOR="Intel10_64lp" + shift # Remove --use-mkl from processing + ;; + esac +done + # Parse command line options -while getopts ":tevhHi:cmspTwr" opt; do +while getopts ":tevhHi:cmpTwr" opt; do case $opt in i) ##### Define installation path ##### echo "${YELLOW}Installation path set to $OPTARG.${NC}" @@ -75,10 +116,6 @@ while getopts ":tevhHi:cmspTwr" opt; do echo "${GREEN}printing make with details.${NC}" VERBOSE="ON" ;; - s) ##### Passing BLA vendor with mkl ##### - echo "${GREEN}MKL as a BLA vendor${NC}" - BLAS_VENDOR="Intel10_64lp" - ;; p) ##### Enabling packaging system for distribution ##### echo "${GREEN}CPACK enabled${NC}" PACKAGE=ON @@ -111,7 +148,8 @@ while getopts ":tevhHi:cmspTwr" opt; do printf "%20s %s\n" "-m :" "to enable using MPI." printf "%20s %s\n" "-v :" "to enable verbose printings." printf "%20s %s\n" "-d :" "to enable debug mode." - printf "%20s %s\n" "-s :" "to manually pass MKL as your bla vendor." + printf "%20s %s\n" "-r :" "to enable Rcpp support." + printf "%20s %s\n" "--use-mkl :" "to force the use of mkl as your bla vendor." printf "%20s %s\n" "-p :" "to enable a packaging system for distribution." printf "%20s %s\n" "-h :" "Help." echo "" @@ -166,7 +204,25 @@ echo "" rm -rf bin/ mkdir bin/ -cmake "$DEVELOPER_WARNINGS" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ +# Check if cmake is installed +if command -v cmake /dev/null 2>&1; then + # Save the installation directory to the variable + cmake_install_dir=$(command -v cmake | xargs dirname) + echo "CMake is installed in: $cmake_install_dir" + cmake_command_bin=cmake +elif [ -x "/Applications/CMake.app/Contents/bin/cmake" ]; then + echo "CMake found in /Applications/CMake.app/Contents/bin/cmake." + cmake_install_dir="/Applications/CMake.app/Contents/bin" + cmake_command_bin="${cmake_install_dir}/cmake" +else + echo "Installing CMake from source" + mkdir "${ABSOLUTE_PATH}/inst/_deps/" + install_dir="${ABSOLUTE_PATH}/inst/_deps/" + install_cmake "$install_dir" + cmake_command_bin="${ABSOLUTE_PATH}/inst/_deps/bin/cmake" +fi + +"$cmake_command_bin" "$DEVELOPER_WARNINGS" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCMAKE_BUILD_TYPE=RELEASE \ -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ -DBUILD_TESTS="${BUILDING_TESTS}" \ @@ -194,7 +250,7 @@ if [ "$USE_R" = "ON" ]; then } # Clean the directory and build the code with the specified options. - cmake --build . -j "$(nproc)" + "$cmake_command_bin" --build . -j 2 if [ "$OS_TYPE" = "darwin"* ]; then cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.dylib" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.dylib -> src" @@ -202,6 +258,8 @@ if [ "$USE_R" = "ON" ]; then cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.so" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.so -> src" fi + "${R_HOME}"/bin/Rscript "${ABSOLUTE_PATH}/cmake/CreateSymbolTable.R" "${ABSOLUTE_PATH}/bin/" "${ABSOLUTE_PATH}/src/symbols.rds" + rm -rf "${ABSOLUTE_PATH:?}/bin/"* fi diff --git a/examples/configurations/CMakeLists.txt b/examples/configurations/CMakeLists.txt index 12341da4..a3a22fc9 100644 --- a/examples/configurations/CMakeLists.txt +++ b/examples/configurations/CMakeLists.txt @@ -10,7 +10,9 @@ # @date 2023-01-31 # Define an executable named "Example_Configurations". -add_executable(Example_Configurations ${CMAKE_CURRENT_SOURCE_DIR}/ConfigurationModule.cpp) +add_executable(Example_Configurations_Setup ${CMAKE_CURRENT_SOURCE_DIR}/SetupConfigurations.cpp) +add_executable(Example_Different_Configurations_Running ${CMAKE_CURRENT_SOURCE_DIR}/RunningWithDifferentConfigurations.cpp) # Link the executable with the ExaGeoStat library and other libraries. -target_link_libraries(Example_Configurations ${PROJECT_NAME}_INTERFACE) \ No newline at end of file +target_link_libraries(Example_Configurations_Setup ${PROJECT_NAME}_INTERFACE) +target_link_libraries(Example_Different_Configurations_Running ${PROJECT_NAME}_INTERFACE) \ No newline at end of file diff --git a/examples/configurations/RunningWithDifferentConfigurations.cpp b/examples/configurations/RunningWithDifferentConfigurations.cpp new file mode 100644 index 00000000..5962adae --- /dev/null +++ b/examples/configurations/RunningWithDifferentConfigurations.cpp @@ -0,0 +1,73 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file RunningWithDifferentConfigurations.cpp + * @brief Demonstrates running ExaGeoStat with various configurations. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-04 +**/ + +#include +#include + +using namespace exageostat::api; +using namespace exageostat::dataunits; + +/** + * @brief Main entry point for the Data Generation & Data Modeling program. + * @details + * @return An integer indicating the success or failure of the program. + */ +int main() { + + // Create a new configurations object. + Configurations configurations; + int N = 16; + configurations.SetProblemSize(N); + configurations.SetKernelName("UnivariateMaternStationary"); + configurations.SetDenseTileSize(9); + configurations.SetMaxMleIterations(10); + configurations.SetTolerance(4); + std::vector lb{0.1, 0.1, 0.1}; + configurations.SetLowerBounds(lb); + + std::vector ub{5, 5, 5}; + configurations.SetUpperBounds(ub); + + std::vector initial_theta{1, 0.1, 0.5}; + configurations.SetInitialTheta(initial_theta); + + // Initialize the ExaGeoStat Hardware + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), + configurations.GetGPUsNumbers()); + // Load data by either read from file or create synthetic data. + std::unique_ptr> data; + ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); + // Modeling module. + ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); + + LOGGER("") + LOGGER("ANOTHER CONFIGURATIONS\n") + // Create a new configurations object. + Configurations configurations2; + N = 10; + configurations2.SetProblemSize(N); + configurations2.SetKernelName("UnivariateMaternStationary"); + configurations2.SetDenseTileSize(5); + configurations2.SetMaxMleIterations(2); + configurations2.SetTolerance(3); + configurations2.SetLowerBounds(lb); + configurations2.SetUpperBounds(ub); + configurations2.SetInitialTheta(initial_theta); + + // Load data by either read from file or create synthetic data. + ExaGeoStat::ExaGeoStatLoadData(hardware, configurations2, data); + // Modeling module. + ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations2, data); + + return 0; +} diff --git a/examples/configurations/ConfigurationModule.cpp b/examples/configurations/SetupConfigurations.cpp similarity index 88% rename from examples/configurations/ConfigurationModule.cpp rename to examples/configurations/SetupConfigurations.cpp index d7f348d3..a06ca247 100644 --- a/examples/configurations/ConfigurationModule.cpp +++ b/examples/configurations/SetupConfigurations.cpp @@ -4,10 +4,10 @@ // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). /** -* @file ConfigurationsMain.cpp +* @file SetupConfigurations.cpp * @brief Demonstrates how to use the Configurations class from the ExaGeoStat software package. * @details This file demonstrates how to use the Configurations class from the ExaGeoStat software package -* to obtain user-defined configurations for generating synthetic data. +* to obtain user-defined configurations. * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-02-04 @@ -26,8 +26,8 @@ using namespace exageostat::common; /** * @brief The main function of the program. * - * This function demonstrates how to use the SyntheticDataConfigurations class from the ExaGeoStat software package - * to obtain user-defined configurations for generating synthetic data. + * This function demonstrates how to use the Configurations class from the ExaGeoStat software package + * to obtain user-defined configurations. * * @param[in] argc The number of command line arguments. * @param[in] argv The command line arguments. @@ -35,12 +35,11 @@ using namespace exageostat::common; */ int main(int argc, char **argv) { - // Create an instance of the SyntheticDataConfigurations class with user-defined configurations. + // Create an instance of the Configurations class with user-defined configurations. Configurations configurations; configurations.InitializeArguments(argc, argv); LOGGER("** These are some examples of the common arguments needed between all modules of ExaGeoStat **") - // Obtain user-defined configurations and print them to the console. int n = configurations.GetProblemSize(); if (n != 0) { @@ -100,5 +99,6 @@ int main(int argc, char **argv) { } VERBOSE("VERBOSE ACTIVATED") + return 0; } \ No newline at end of file diff --git a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp index 10740540..0c430048 100644 --- a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp +++ b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp @@ -21,6 +21,7 @@ #include #include +#include namespace exageostat::adapters { @@ -52,24 +53,53 @@ namespace exageostat::adapters { */ Configurations * R_InitializeArguments(const int &aProblemSize, const std::string &aKernelName, const std::vector &aTileSize, - const std::vector &aP_QGrid, const int &aTimeSlot, const std::string &aComputation, - const std::string &aPrecision, const std::vector &aCoresGPUsNumber, const int &aBand, - const int &aMaxRank, const std::vector &aInitialTheta, - const std::vector> &aLowerUpperBounds, - const std::vector &aEstimatedTheta, const std::string &aVerbose, - const std::string &aDimension, const int &aMaxMleIterations, const double &aTolerance, - const std::vector &aPrediction + const std::vector &aP_QGrid, const int &aTimeSlot, const std::string &aComputation, + const std::string &aPrecision, const std::vector &aCoresGPUsNumber, const int &aBand, + const int &aMaxRank, const std::vector &aInitialTheta, + const std::vector> &aLowerUpperBounds, + const std::vector &aEstimatedTheta, const std::string &aVerbose, + const std::string &aDimension, const int &aMaxMleIterations, const double &aTolerance, + const std::vector &aPrediction ); /** - * @brief Executes the ExaGeoStat main API functions based on provided hardware and configurations. - * @details This function serves as an entry point to the ExaGeoStat library, allowing users to execute statistical models and algorithms by passing in hardware configurations and computational settings. - * It integrates the hardware layer with the statistical computations, ensuring that the ExaGeoStat algorithms can efficiently utilize the available resources. - * This function is crucial for the seamless operation of the ExaGeoStat software, facilitating the execution of complex statistical tasks on diverse hardware configurations. + * @brief Function to load ExaGeoStat data. + * @details This function loads data into an ExaGeoStatData object using the provided hardware configuration and computational settings. + * It is designed to initialize the data structure necessary for subsequent statistical model operations within the ExaGeoStat framework. * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. + * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data will be stored. + * @return A pointer to an ExaGeoStatData object containing the loaded data. + * + */ + ExaGeoStatData *R_ExaGeoStatLoadData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData); + + /** + * @brief Function to model ExaGeoStat data. + * @details This function applies the model configurations specified in apConfigurations to the data stored in apData, using the hardware setup specified by apHardware. + * It prepares the ExaGeoStatData object for statistical analysis and prediction tasks, adjusting the internal data representations and parameters as needed. + * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. + * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. + * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data will be stored. + * @return A pointer to an ExaGeoStatData object containing the loaded data. + * + */ + ExaGeoStatData *R_ExaGeoStatModelData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData); + + /** + * @brief Function to predict using ExaGeoStat data. + * @details This function performs predictions based on the modeled ExaGeoStatData object. + * It utilizes the hardware configuration and computational settings defined by apHardware and apConfigurations, respectively, to execute prediction algorithms on the data stored in apData. + * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. + * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. + * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data will be stored. + * @return A pointer to an ExaGeoStatData object containing the loaded data. + * */ - void R_ExaGeoStatAPI(ExaGeoStatHardware *apHardware, Configurations *apConfigurations); + ExaGeoStatData *R_ExaGeoStatPredictData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData); } #endif //EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP diff --git a/inst/include/api/ExaGeoStat.hpp b/inst/include/api/ExaGeoStat.hpp index c967dd39..b2f93ad3 100644 --- a/inst/include/api/ExaGeoStat.hpp +++ b/inst/include/api/ExaGeoStat.hpp @@ -41,7 +41,7 @@ namespace exageostat::api { */ static void ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - std::unique_ptr> &aData); + std::unique_ptr> &aData); /** * @brief Models Data whether it's synthetic data or real. @@ -49,12 +49,12 @@ namespace exageostat::api { * @param[in] aConfigurations Reference to Configurations object containing user input data. * @param[in] aData Reference to an ExaGeoStatData object containing needed descriptors, and locations. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. - * @return void + * @return the last optimum value of MLE. * */ static T ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, T *apMeasurementsMatrix = nullptr); @@ -77,7 +77,7 @@ namespace exageostat::api { */ static void ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, T *apMeasurementsMatrix = nullptr); private: diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index 146f7fb6..7944b751 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -127,6 +127,15 @@ namespace exageostat::common { HICMA_DESCRIPTOR = 1 }; + /** + * @enum Data source Type + * @brief Enum denoting the data source Type. + */ + enum DataSourceType { + SYNTHETIC = 0, + CSV_FILE = 1 + }; + /** * @enum Descriptor Name * @brief Enum denoting all Descriptors Names. diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index 733b119f..75a7c9f4 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -74,19 +74,20 @@ class Configurations { Configurations(); /** - * @brief Virtual destructor to allow calls to the correct concrete destructor. + * @brief destructor to allow calls to the correct concrete destructor. * */ - ~Configurations() = default; + ~Configurations(); /** * @brief Initialize the module arguments. * @param[in] aArgC The number of arguments being passed into the program from the command line. * @param[in] apArgV The array of arguments. + * @param[in] aEnableR check if R is enabled * @details This method initializes the command line arguments and set default values for unused args. * */ - void InitializeArguments(const int &aArgC, char **apArgV); + void InitializeArguments(const int &aArgC, char **apArgV, const bool &aEnableR = false); /** * @brief Initialize the module arguments. @@ -435,7 +436,8 @@ class Configurations { static exageostat::common::Verbose mVerbosity; //// Used bool for init theta static bool mIsThetaInit; - + //// Used bool for R allocated memory on heap + static bool mHeapAllocated; }; #endif //EXAGEOSTAT_CPP_CONFIGURATIONS_HPP diff --git a/inst/include/data-generators/DataGenerator.hpp b/inst/include/data-generators/DataGenerator.hpp index 6c5dfd3d..d040caa7 100644 --- a/inst/include/data-generators/DataGenerator.hpp +++ b/inst/include/data-generators/DataGenerator.hpp @@ -14,8 +14,6 @@ #ifndef EXAGEOSTAT_CPP_DATAGENERATOR_HPP #define EXAGEOSTAT_CPP_DATAGENERATOR_HPP -#include - #include #include @@ -41,7 +39,7 @@ namespace exageostat::generators { * @return unique Pointer to a populated data. * */ - virtual std::unique_ptr> + virtual std::unique_ptr> CreateData(Configurations &aConfigurations, const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) = 0; @@ -64,8 +62,8 @@ namespace exageostat::generators { virtual ~DataGenerator(); protected: - /// Used bool identifying type of generation. - static bool mIsSynthetic; + /// Used enum for data generators types. + static common::DataSourceType aDataSourceType; }; /** diff --git a/inst/include/data-generators/LocationGenerator.hpp b/inst/include/data-generators/LocationGenerator.hpp new file mode 100644 index 00000000..023b6ba4 --- /dev/null +++ b/inst/include/data-generators/LocationGenerator.hpp @@ -0,0 +1,72 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file LocationGenerator.hpp + * @brief Generates and manages spatial locations for ExaGeoStat. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-04 +**/ + +#ifndef EXAGEOSTATCPP_LOCATIONGENERATOR_HPP +#define EXAGEOSTATCPP_LOCATIONGENERATOR_HPP + +#include + +namespace exageostat::generators { + + /** + * @class LocationGenerator + * @brief Generates spatial locations based on given parameters. + * @tparam T Data Type: float or double + * + */ + template + class LocationGenerator { + + public: + + /** + * @brief Generates the data locations. + * @details This method generates the X, Y, and Z variables used to define the locations of the data points. + * @param[in] aN The number of data points. + * @param[in] aTimeSlot The time slot. + * @param[in] aDimension The dimension of the locations. + * @param[out] aLocations Reference to the Locations object where the generated data will be stored. + * @return void + * + */ + static void GenerateLocations(const int &aN, const int &aTimeSlot, const common::Dimension &aDimension, dataunits::Locations &aLocations); + + /** + * @brief Generate uniform distribution between rangeLow , rangeHigh. + * @param[in] aRangeLow The Lower range. + * @param[in] aRangeHigh The Higher range. + * @return The scaled uniform distribution between the two bounds. + * + */ + static T UniformDistribution(const T &aRangeLow, const T &aRangeHigh); + + /** + * @brief Sort locations in Morton order (input points must be in [0;1]x[0;1] square]). + * @param[in] aN The problem size divided by P-Grid. + * @param[in] aDimension Dimension of locations. + * @param[in,out] aLocations Locations to be sorted. + * @return void + * + */ + static void SortLocations(const int &aN, const common::Dimension &aDimension, dataunits::Locations &aLocations); + }; + + /** + * @brief Instantiates the Data Generator class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(LocationGenerator) +}//namespace exageostat + +#endif //EXAGEOSTATCPP_LOCATIONGENERATOR_HPP diff --git a/inst/include/data-generators/concrete/CSVDataGenerator.hpp b/inst/include/data-generators/concrete/CSVDataGenerator.hpp deleted file mode 100644 index 0547bb93..00000000 --- a/inst/include/data-generators/concrete/CSVDataGenerator.hpp +++ /dev/null @@ -1,97 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file CSVDataGenerator.hpp - * @brief A class for generating synthetic data. - * @version 1.1.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @date 2023-02-14 -**/ - -#ifndef EXAGEOSTAT_CPP_CSVDATAGENERATOR_HPP -#define EXAGEOSTAT_CPP_CSVDATAGENERATOR_HPP - -#include - -namespace exageostat::generators::csv { - - /** - * @class CSVDataGenerator - * @brief A class for creating data by reading CSV files. - * @tparam T Data Type: float or double - */ - template - class CSVDataGenerator : public DataGenerator { - public: - - /** - * @brief Get a pointer to the singleton instance of the CSVDataGenerator class. - * @return A pointer to the instance of the CSVDataGenerator class. - * - */ - static CSVDataGenerator *GetInstance(); - - /** - * @brief Creates the data by reading a CSV file. - * @copydoc DataGenerator::CreateData() - * - */ - std::unique_ptr> CreateData(Configurations &, - const ExaGeoStatHardware &aHardware, - exageostat::kernels::Kernel &aKernel) override; - - /** - * @brief Reads CSV files containing 2D, 3D, or ST locations, and measurements vector. - * @param aConfigurations Reference to the Configurations object. - * @param aMeasurementsMatrix Reference to the Measurement matrix to be filled with read data. - * @param aXLocations Reference to the location's x coordinates matrix to be filled with read data. - * @param aYLocations Reference to the location's y coordinates matrix to be filled with read data. - * @param aZLocations Reference to the location's z coordinates matrix to be filled with read data. - * @param[in] aP the P value of the kernel multiplied by time slot. - * @return void - */ - void ReadData(Configurations &aConfigurations, std::vector &aMeasurementsMatrix, - std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, - const int &aP); - - /** - * @brief Release the singleton instance of the CSVDataGenerator class. - * @return void - * - */ - static void ReleaseInstance(); - - private: - /** - * @brief Constructor for the CSVDataGenerator class. - * @return void - * - */ - CSVDataGenerator() = default; - - /** - * @brief Default destructor. - * - */ - ~CSVDataGenerator() override = default; - - /** - * @brief Pointer to the singleton instance of the CSVDataGenerator class. - * - */ - static CSVDataGenerator *mpInstance; - - }; - - /** - * @brief Instantiates the CSV Data Generator class for float and double types. - * @tparam T Data Type: float or double - * - */ - EXAGEOSTAT_INSTANTIATE_CLASS(CSVDataGenerator) -} -#endif //EXAGEOSTAT_CPP_CSVDATAGENERATOR_HPP \ No newline at end of file diff --git a/inst/include/data-generators/concrete/SyntheticGenerator.hpp b/inst/include/data-generators/concrete/SyntheticGenerator.hpp index c3405235..62f86b4c 100644 --- a/inst/include/data-generators/concrete/SyntheticGenerator.hpp +++ b/inst/include/data-generators/concrete/SyntheticGenerator.hpp @@ -38,74 +38,16 @@ namespace exageostat::generators::synthetic { */ static SyntheticGenerator *GetInstance(); - /** - * @brief Generates the data locations. - * @details This method generates the X, Y, and Z variables used to define the locations of the data points. - * @param[in] aN The number of data points. - * @param[in] aTimeSlot The time slot. - * @param[in] aDimension The dimension of the locations. - * @param[out] aLocations Reference to the Locations object where the generated data will be stored. - * @return void - * - */ - void GenerateLocations(const int &aN, const int &aTimeSlot, const common::Dimension &aDimension, - dataunits::Locations &aLocations); - /** * @brief Creates the data by synthetically generating it. * @copydoc DataGenerator::CreateData() * */ - std::unique_ptr> + std::unique_ptr> CreateData(Configurations &aConfigurations, const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) override; - /** - * @brief Generate uniform distribution between rangeLow , rangeHigh. - * @param[in] aRangeLow The Lower range. - * @param[in] aRangeHigh The Higher range. - * @return The scaled uniform distribution between the two bounds. - * - */ - static T UniformDistribution(const T &aRangeLow, const T &aRangeHigh); - - /** - * @brief Sort locations in Morton order (input points must be in [0;1]x[0;1] square]). - * @param[in] aN The problem size divided by P-Grid. - * @param[in] aDimension Dimension of locations. - * @param[in,out] aLocations Locations to be sorted. - * @return void - * - */ - void - SortLocations(const int &aN, const common::Dimension &aDimension, dataunits::Locations &aLocations); - - /** - * @brief Spread bits by three spaces. - * @param[in] aInputByte The input 64 bit to be spread. - * @return The byte after being spread. - * - */ - static uint64_t SpreadBits(uint64_t aInputByte); - - /** - * @brief Reverse Spread bits operation. - * @param[in] aInputByte The input spread 64 bit to be compacted. - * @return The byte after being compacted. - * - */ - static uint64_t ReverseSpreadBits(uint64_t aInputByte); - - /** - * @brief Compares two Unit64 values - * @param[in] aFirstValue Constant reference to the first input 64 bit value. - * @param[in] aSecondValue Constant reference to the second input 64 bit value. - * @return True if the second value is bigger than the first value, false otherwise. - * - */ - static bool CompareUint64(const uint64_t &aFirstValue, const uint64_t &aSecondValue); - /** * @brief Release the singleton instance of the SyntheticGenerator class. * @return void diff --git a/inst/include/data-loader/DataLoader.hpp b/inst/include/data-loader/DataLoader.hpp new file mode 100644 index 00000000..b14e5371 --- /dev/null +++ b/inst/include/data-loader/DataLoader.hpp @@ -0,0 +1,66 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file DataLoader.hpp + * @brief Manages data loading operations for ExaGeoStat. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-04 +**/ + +#ifndef EXAGEOSTATCPP_DATALOADER_HPP +#define EXAGEOSTATCPP_DATALOADER_HPP + +#include + +namespace exageostat::dataLoader { + + /** + * @class DataLoader + * @brief Extends DataGenerator to include data loading functionalities. + * @tparam T Data Type: float or double + * + */ + template + class DataLoader : public generators::DataGenerator { + + public: + + /** + * @brief Creates the data by synthetically generating it. + * @copydoc DataGenerator::CreateData() + * + */ + std::unique_ptr> + CreateData(Configurations &aConfigurations, const ExaGeoStatHardware &aHardware, + kernels::Kernel &aKernel) override; + + /** + * @brief Reads data from external sources into ExaGeoStat format. + * @param aConfigurations Configuration settings for data loading. + * @param aMeasurementsMatrix Vector to store measurement values. + * @param aXLocations Vector to store X coordinates of locations. + * @param aYLocations Vector to store Y coordinates of locations. + * @param aZLocations Vector to store Z coordinates of locations (if applicable). + * @param aP Partition index for distributed data loading. + */ + virtual void + ReadData(Configurations &aConfigurations, std::vector &aMeasurementsMatrix, std::vector &aXLocations, + std::vector &aYLocations, std::vector &aZLocations, const int &aP) = 0; + + }; + + /** + * @brief Instantiates the Synthetic Data Generator class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(DataLoader) + +} // namespace exageostat + +#endif //EXAGEOSTATCPP_DATALOADER_HPP diff --git a/inst/include/data-loader/concrete/CSVLoader.hpp b/inst/include/data-loader/concrete/CSVLoader.hpp new file mode 100644 index 00000000..56d84334 --- /dev/null +++ b/inst/include/data-loader/concrete/CSVLoader.hpp @@ -0,0 +1,84 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file CSVLoader.hpp + * @brief A class for generating synthetic data. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-04 +**/ + +#ifndef EXAGEOSTAT_CPP_CSVDATALOADER_HPP +#define EXAGEOSTAT_CPP_CSVDATALOADER_HPP + +#include +#include + +namespace exageostat::dataLoader::csv { + + /** + * @class CSVLoader + * @brief A class for creating data by reading CSV files. + * @tparam T Data Type: float or double + */ + template + class CSVLoader : public DataLoader { + public: + + /** + * @brief Get a pointer to the singleton instance of the CSVLoader class. + * @return A pointer to the instance of the CSVLoader class. + * + */ + static CSVLoader *GetInstance(); + + /** + * @brief Reads data from external sources into ExaGeoStat format. + * @copydoc DataLoader::ReadData() + * + */ + void ReadData(Configurations &aConfigurations, std::vector &aMeasurementsMatrix, + std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, + const int &aP) override; + + /** + * @brief Release the singleton instance of the CSVLoader class. + * @return void + * + */ + static void ReleaseInstance(); + + private: + /** + * @brief Constructor for the CSVLoader class. + * @return void + * + */ + CSVLoader() = default; + + /** + * @brief Default destructor. + * + */ + ~CSVLoader() override = default; + + /** + * @brief Pointer to the singleton instance of the CSVLoader class. + * + */ + static CSVLoader *mpInstance; + + }; + + /** + * @brief Instantiates the CSV Data Generator class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(CSVLoader) +} +#endif //EXAGEOSTAT_CPP_CSVDATALOADER_HPP \ No newline at end of file diff --git a/inst/include/data-units/DescriptorData.hpp b/inst/include/data-units/DescriptorData.hpp index d5d9f88b..7cc64c37 100644 --- a/inst/include/data-units/DescriptorData.hpp +++ b/inst/include/data-units/DescriptorData.hpp @@ -49,7 +49,7 @@ namespace exageostat::dataunits { /** * @brief Destructor for DescriptorData. */ - virtual ~DescriptorData(); + ~DescriptorData(); /** * @brief Get the base descriptor. diff --git a/inst/include/data-units/ExaGeoStatData.hpp b/inst/include/data-units/ExaGeoStatData.hpp index d3c5ce0c..14a02ec6 100644 --- a/inst/include/data-units/ExaGeoStatData.hpp +++ b/inst/include/data-units/ExaGeoStatData.hpp @@ -20,94 +20,91 @@ #include #include -namespace exageostat::dataunits { +/** + * @Class ExaGeoStatData + * @brief Manages geo-statistical data with functions for location and descriptor manipulation + * @tparam T Data Type: float or double + */ +template +class ExaGeoStatData { + +public: + /** + * @brief Constructor for ExaGeoStatData. + * @param[in] aSize The size of the data. + * @param[in] aDimension The dimension of the data. + */ + ExaGeoStatData(const int &aSize, const exageostat::common::Dimension &aDimension); /** - * @Class ExaGeoStatData - * @brief Manages geo-statistical data with functions for location and descriptor manipulation - * @tparam T Data Type: float or double + * @brief Constructor for ExaGeoStatData. + * @param[in] aSize The size of the data. + * @param[in] aDimension The dimension of the data. */ - template - class ExaGeoStatData { - - public: - /** - * @brief Constructor for ExaGeoStatData. - * @param[in] aSize The size of the data. - * @param[in] aDimension The dimension of the data. - */ - ExaGeoStatData(const int &aSize, const exageostat::common::Dimension &aDimension); - - /** - * @brief Constructor for ExaGeoStatData. - * @param[in] aSize The size of the data. - * @param[in] aDimension The dimension of the data. - */ - ExaGeoStatData(const int &aSize, const std::string &aDimension); - - /** - * @brief Default constructor for ExaGeoStatData. - */ - ExaGeoStatData() = default; - - /** - * @brief Destructor for ExaGeoStatData. - */ - ~ExaGeoStatData(); - - /** - * @brief Get the locations. - * @return Pointer to the Locations object. - */ - Locations *GetLocations(); - - /** - * @brief Set the locations. - * @param[in] aLocation Pointer to the Locations object. - */ - void SetLocations(Locations &aLocation); - - /** - * @brief Get the descriptor data. - * @return Pointer to the DescriptorData object. - */ - DescriptorData *GetDescriptorData(); - - /** - * @brief Setter for the number of performed MLE iterations. - * @param[in] aMleIterations number of performed MLE iterations. - * @return void - */ - void SetMleIterations(const int &aMleIterations); - - /** - * @brief Get the number of performed MLE iterations. - * @return Pointer to the DescriptorData object. - */ - int GetMleIterations(); - - /** - * @brief Calculates Median Locations. - * @param[in] aKernelName Name of the Kernel used. - * @param[out] aLocations Location object to save medianLocations in. - * @return void - */ - void CalculateMedianLocations(const std::string &aKernelName, dataunits::Locations &aLocations); - - private: - //// Used descriptor data. - DescriptorData *mpDescriptorData = nullptr; - //// Used locations data. - Locations *mpLocations = nullptr; - //// Current number of performed MLE iterations. - int mMleIterations = 0; - }; + ExaGeoStatData(const int &aSize, const std::string &aDimension); /** - * @brief Instantiates the ExaGeoStatData class for float and double types. - * @tparam T Data Type: float or double + * @brief Default constructor for ExaGeoStatData. */ - EXAGEOSTAT_INSTANTIATE_CLASS(ExaGeoStatData) -} // namespace exageostat + ExaGeoStatData() = default; + + /** + * @brief Destructor for ExaGeoStatData. + */ + ~ExaGeoStatData(); + + /** + * @brief Get the locations. + * @return Pointer to the Locations object. + */ + exageostat::dataunits::Locations *GetLocations(); + + /** + * @brief Set the locations. + * @param[in] aLocation Pointer to the Locations object. + */ + void SetLocations(exageostat::dataunits::Locations &aLocation); + + /** + * @brief Get the descriptor data. + * @return Pointer to the DescriptorData object. + */ + exageostat::dataunits::DescriptorData *GetDescriptorData(); + + /** + * @brief Setter for the number of performed MLE iterations. + * @param[in] aMleIterations number of performed MLE iterations. + * @return void + */ + void SetMleIterations(const int &aMleIterations); + + /** + * @brief Get the number of performed MLE iterations. + * @return Pointer to the DescriptorData object. + */ + int GetMleIterations(); + + /** + * @brief Calculates Median Locations. + * @param[in] aKernelName Name of the Kernel used. + * @param[out] aLocations Location object to save medianLocations in. + * @return void + */ + void CalculateMedianLocations(const std::string &aKernelName, exageostat::dataunits::Locations &aLocations); + using MyTemplateClassDouble = ExaGeoStatData; + +private: + //// Used descriptor data. + exageostat::dataunits::DescriptorData *mpDescriptorData = nullptr; + //// Used locations data. + exageostat::dataunits::Locations *mpLocations = nullptr; + //// Current number of performed MLE iterations. + int mMleIterations = 0; +}; +/** + * @brief Instantiates the ExaGeoStatData class for float and double types. + * @tparam T Data Type: float or double + */ +EXAGEOSTAT_INSTANTIATE_CLASS(ExaGeoStatData) #endif //EXAGEOSTATCPP_EXAGEOSTATDATA_HPP \ No newline at end of file diff --git a/inst/include/data-units/Locations.hpp b/inst/include/data-units/Locations.hpp index 77f0c0ae..43519585 100644 --- a/inst/include/data-units/Locations.hpp +++ b/inst/include/data-units/Locations.hpp @@ -43,10 +43,10 @@ namespace exageostat::dataunits { Locations(const Locations &aLocations) = default; /** - * @brief Virtual destructor to allow calls to the correct concrete destructor. + * @brief destructor for Locations. * */ - virtual ~Locations(); + ~Locations(); /** * @brief Setter for LocationX. diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index 4f7af247..549f7a17 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -9,7 +9,7 @@ * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-01-20 + * @date 2024-01-24 **/ #ifndef EXAGEOSTATCPP_EXAGEOSTATHARDWARE_HPP @@ -30,7 +30,7 @@ class ExaGeoStatHardware { * @param[in] aGpuNumber The number of GPUs to use for the solver. * */ - ExaGeoStatHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); + explicit ExaGeoStatHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); /** * @brief Constructor for ExaGeoStatHardware. @@ -38,12 +38,18 @@ class ExaGeoStatHardware { * @param[in] aCoreNumber The number of CPU cores to use for the solver. * @param[in] aGpuNumber The number of GPUs to use for the solver. */ - ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber); + explicit ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber); + + /** + * @brief A Finalize caller for Hardware. + * @return void. + */ + void FinalizeHardware(); /** * @brief Destructor for ExaGeoStatHardware. */ - virtual ~ExaGeoStatHardware(); + ~ExaGeoStatHardware(); /** * @brief Initializes hardware configuration. @@ -51,25 +57,21 @@ class ExaGeoStatHardware { * @param[in] aCoreNumber The number of CPU cores to use for the solver. * @param[in] aGpuNumber The number of GPUs to use for the solver. */ - void InitHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); + static void InitHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); /** * @brief Get the Chameleon hardware context. * @return Pointer to the hardware context. * */ - [[nodiscard]] void *GetChameleonContext() const; - -#ifdef USE_HICMA - -/** - * @brief Get the Hicma hardware context. - * @return Pointer to the hardware context. - * - */ - [[nodiscard]] void *GetHicmaContext() const; + [[nodiscard]] static void *GetChameleonContext(); -#endif + /** + * @brief Get the HiCMA hardware context. + * @return Pointer to the hardware context. + * + */ + [[nodiscard]] static void *GetHicmaContext(); /** * @brief Get the hardware context. @@ -77,13 +79,13 @@ class ExaGeoStatHardware { * @return Pointer to the hardware context. * */ - [[nodiscard]] void *GetContext(exageostat::common::Computation aComputation) const; - -private: - //// Used Pointer to the Chameleon hardware context. - void *mpChameleonContext = nullptr; - //// Used Pointer to the Hicma hardware context. - void *mpHicmaContext = nullptr; -}; + [[nodiscard]] static void *GetContext(exageostat::common::Computation aComputation); + + private: + //// Used Pointer to the Chameleon hardware context. + static void *mpChameleonContext; + //// Used Pointer to the Hicma hardware context. + static void *mpHicmaContext; + }; #endif // EXAGEOSTATCPP_EXAGEOSTATHARDWARE_HPP \ No newline at end of file diff --git a/inst/include/helpers/BasselFunction.hpp b/inst/include/helpers/BasselFunction.hpp new file mode 100644 index 00000000..541a6974 --- /dev/null +++ b/inst/include/helpers/BasselFunction.hpp @@ -0,0 +1,69 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file BasselFunction.hpp + * @brief This file contains the BasselFunction class which provides methods for computing derivatives of the modified Bessel function of the second kind. These functions are crucial in statistical and mathematical computations, especially in fields such as geostatistics and spatial analysis. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2023-01-24 +**/ + +#ifndef EXAGEOSTATCPP_BASSELFUNCTION_HPP +#define EXAGEOSTATCPP_BASSELFUNCTION_HPP + +#include + +namespace exageostat::helpers { + + /** + * @class BasselFunction + * @brief The BasselFunction class provides methods for computing various derivatives of the modified Bessel function of the second kind, \( K_{\nu} \). This class is templated to support both float and double data types, enabling precision-based computations as required by different applications. + * @tparam T Data Type: float or double + * + */ + template + class BasselFunction { + + public: + /** + * @brief Calculates the derivative of the modified Bessel function of the second kind (K_nu) with respect to its order, evaluated at input_value and order aOrder. + * @param[in] aOrder The order of the Bessel function. + * @param[in] aInputValue The input value at which to evaluate the derivative. + * @return The value of the derivative of K_nu with respect to its order, evaluated at input_value and order aOrder. + * + */ + static T CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue); + + /** + * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. + * @param[in] aOrder The order of the Bessel function. + * @param[in] aInputValue The input value at which to evaluate the second derivative. + * @return The value of the second derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. + * + */ + static T CalculateSecondDerivativeBesselNu(const T &aOrder, const T &aInputValue); + + /** + * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. + * @param[in] aOrder The order of the Bessel function. + * @param[in] aInputValue The input value at which to evaluate the derivative. + * @return The value of the derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. + * + */ + static T CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aInputValue); + + }; + + /** + * @brief Instantiates the DiskWriter class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(BasselFunction) +} + +#endif //EXAGEOSTATCPP_BASSELFUNCTION_HPP diff --git a/inst/include/helpers/ByteHandler.hpp b/inst/include/helpers/ByteHandler.hpp new file mode 100644 index 00000000..f8139aa6 --- /dev/null +++ b/inst/include/helpers/ByteHandler.hpp @@ -0,0 +1,48 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ByteHandler.hpp + * @brief Implementation of byte manipulation functions for ExaGeoStat. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-01-24 +**/ + +#ifndef EXAGEOSTATCPP_BYTEHANDLER_HPP +#define EXAGEOSTATCPP_BYTEHANDLER_HPP + +#include + +namespace exageostat::helpers { + + /** + * @brief Spread bits by three spaces. + * @param[in] aInputByte The input 64 bit to be spread. + * @return The byte after being spread. + * + */ + uint64_t SpreadBits(uint64_t aInputByte); + + /** + * @brief Reverse Spread bits operation. + * @param[in] aInputByte The input spread 64 bit to be compacted. + * @return The byte after being compacted. + * + */ + uint64_t ReverseSpreadBits(uint64_t aInputByte); + + /** + * @brief Compares two Unit64 values + * @param[in] aFirstValue Constant reference to the first input 64 bit value. + * @param[in] aSecondValue Constant reference to the second input 64 bit value. + * @return True if the second value is bigger than the first value, false otherwise. + * + */ + bool CompareUint64(const uint64_t &aFirstValue, const uint64_t &aSecondValue); + +} +#endif //EXAGEOSTATCPP_BYTEHANDLER_HPP diff --git a/inst/include/helpers/DiskWriter.hpp b/inst/include/helpers/DiskWriter.hpp index 32e7715a..7d43d834 100644 --- a/inst/include/helpers/DiskWriter.hpp +++ b/inst/include/helpers/DiskWriter.hpp @@ -9,7 +9,7 @@ * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-06-08 + * @date 2024-01-24 **/ #ifndef EXAGEOSTATCPP_DISKWRITER_HPP diff --git a/inst/include/kernels/Kernel.hpp b/inst/include/kernels/Kernel.hpp index 8e7043d9..dabb3261 100644 --- a/inst/include/kernels/Kernel.hpp +++ b/inst/include/kernels/Kernel.hpp @@ -30,6 +30,7 @@ extern "C" { #include #include #include +#include /** * @def EARTH_RADIUS @@ -100,33 +101,6 @@ namespace exageostat::kernels { dataunits::Locations &aLocation2, dataunits::Locations &aLocation3, T *apLocalTheta, const int &aDistanceMetric) = 0; - /** - * @brief Calculates the derivative of the modified Bessel function of the second kind (K_nu) with respect to its order, evaluated at input_value and order aOrder. - * @param[in] aOrder The order of the Bessel function. - * @param[in] aInputValue The input value at which to evaluate the derivative. - * @return The value of the derivative of K_nu with respect to its order, evaluated at input_value and order aOrder. - * - */ - static T CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue); - - /** - * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. - * @param[in] aOrder The order of the Bessel function. - * @param[in] aInputValue The input value at which to evaluate the second derivative. - * @return The value of the second derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. - * - */ - static T CalculateSecondDerivativeBesselNu(const T &aOrder, const T &aInputValue); - - /** - * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. - * @param[in] aOrder The order of the Bessel function. - * @param[in] aInputValue The input value at which to evaluate the derivative. - * @return The value of the derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. - * - */ - static T CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aInputValue); - /** * @brief Returns the value of the parameter P used by the kernel function. * @return The value of P (Variables Number). diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp index 2a0f66da..86010997 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp @@ -81,7 +81,7 @@ namespace exageostat::linearAlgebra { * */ void InitiatePredictionDescriptors(Configurations &aConfigurations, - std::unique_ptr> &aData, const int &aP); + std::unique_ptr> &aData, const int &aP); /** * @brief Initializes the descriptors necessary for the Prediction Auxiliary function MLE-MLOE-MMOM. @@ -93,7 +93,7 @@ namespace exageostat::linearAlgebra { * */ void InitiateMLOEMMOMDescriptors(Configurations &aConfigurations, - std::unique_ptr> &aData, const int &aP); + std::unique_ptr> &aData, const int &aP); /** * @brief Generates synthetic data. @@ -106,7 +106,7 @@ namespace exageostat::linearAlgebra { */ void GenerateSyntheticData(Configurations &aConfigurations, const ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, + std::unique_ptr> &aData, const kernels::Kernel &aKernel); /** @@ -153,7 +153,7 @@ namespace exageostat::linearAlgebra { */ void GenerateObservationsVector(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, dataunits::Locations *apLocation1, dataunits::Locations *apLocation2, dataunits::Locations *apLocation3, const int &aDistanceMetric, const kernels::Kernel &aKernel); @@ -170,7 +170,7 @@ namespace exageostat::linearAlgebra { * */ virtual T ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, + std::unique_ptr> &aData, Configurations &aConfigurations, const double *apTheta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) = 0; @@ -332,7 +332,7 @@ namespace exageostat::linearAlgebra { * @param[in] aKernel Reference to the kernel object to use. * @return the prediction Mean Square Error (MSPE). */ - T *ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, + T *ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, const ExaGeoStatHardware &aHardware, @@ -357,7 +357,7 @@ namespace exageostat::linearAlgebra { * @param[in] aKernel Reference to the kernel object to use. * @return the prediction Mean Square Error (MSPE). */ - T *ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, T *apTheta, + T *ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, const ExaGeoStatHardware &aHardware, @@ -436,7 +436,7 @@ namespace exageostat::linearAlgebra { * @return void */ void ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, const ExaGeoStatHardware &aHardware, T *apTruthTheta, T *apEstimatedTheta, dataunits::Locations &aMissLocations, dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); @@ -452,7 +452,7 @@ namespace exageostat::linearAlgebra { */ T * ExaGeoStatFisherTile(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, const ExaGeoStatHardware &aHardware, T *apTheta, const kernels::Kernel &aKernel); diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp index 32b1c86d..84fd332f 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp @@ -33,7 +33,7 @@ namespace exageostat::linearAlgebra { * @brief Calculates the log likelihood value of a given value theta. * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() */ - T ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, + T ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) override; diff --git a/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp index 9f8a8600..dfa3594f 100644 --- a/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp @@ -45,7 +45,7 @@ namespace exageostat::linearAlgebra::tileLowRank { * @param[in] aConfigurations Reference to the Configurations object. * @param[in] aP the P value of the kernel multiplied by time slot. */ - void SetModelingDescriptors(std::unique_ptr> &aData, + void SetModelingDescriptors(std::unique_ptr> &aData, Configurations &aConfigurations, const int &aP); /** @@ -53,7 +53,7 @@ namespace exageostat::linearAlgebra::tileLowRank { * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() */ T ExaGeoStatMLETile(const ExaGeoStatHardware &apHardware, - std::unique_ptr> &aData, + std::unique_ptr> &aData, Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) override; diff --git a/inst/include/prediction/Prediction.hpp b/inst/include/prediction/Prediction.hpp index 1e0f402e..d880f125 100644 --- a/inst/include/prediction/Prediction.hpp +++ b/inst/include/prediction/Prediction.hpp @@ -29,16 +29,6 @@ namespace exageostat::prediction { class Prediction { public: - /** - * @brief Default constructor for Prediction class. - */ - Prediction() = default; - - /** - * @brief Default destructor for Prediction class. - */ - ~Prediction() = default; - /** * @brief Takes care of calling the MSPE function, and the appropriate auxiliary function. * @param[in] aHardware Reference to Hardware configuration for the ExaGeoStat solver. @@ -48,10 +38,10 @@ namespace exageostat::prediction { * @param[in] aKernel Reference to the kernel object to use. * @return */ - void PredictMissingData(const ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, - Configurations &aConfigurations, T *apMeasurementsMatrix, - const kernels::Kernel &aKernel); + static void PredictMissingData(const ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, + Configurations &aConfigurations, + T *apMeasurementsMatrix, const kernels::Kernel &aKernel); /** * @brief Initializes needed pointers for prediction. @@ -66,12 +56,13 @@ namespace exageostat::prediction { * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ - void InitializePredictionArguments(Configurations &aConfigurations, - std::unique_ptr> &aData, - std::unique_ptr> &aLinearAlgebraSolver, - T *apZObs, T *apZActual, exageostat::dataunits::Locations &aMissLocation, - exageostat::dataunits::Locations &aObsLocation, T *apMeasurementsMatrix, - const int &aP); + static void InitializePredictionArguments(Configurations &aConfigurations, + std::unique_ptr> &aData, + std::unique_ptr> &aLinearAlgebraSolver, + T *apZObs, T *apZActual, + exageostat::dataunits::Locations &aMissLocation, + exageostat::dataunits::Locations &aObsLocation, + T *apMeasurementsMatrix, const int &aP); }; diff --git a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp index f0417219..c39c3d4b 100644 --- a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp +++ b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp @@ -28,16 +28,6 @@ namespace exageostat::prediction { class PredictionAuxiliaryFunctions { public: - /** - * @brief Default constructor for PredictionAuxiliaryFunctions - */ - PredictionAuxiliaryFunctions() = default; - - /** - * @brief Default destructor for PredictionAuxiliaryFunctions - */ - ~PredictionAuxiliaryFunctions() = default; - /** * @brief implements the Inverse Distance Weighting (IDW) interpolation method * for predicting missing values based on available observed values. diff --git a/inst/include/prediction/PredictionHelpers.hpp b/inst/include/prediction/PredictionHelpers.hpp index 50ab483c..c1fdbe02 100644 --- a/inst/include/prediction/PredictionHelpers.hpp +++ b/inst/include/prediction/PredictionHelpers.hpp @@ -42,7 +42,7 @@ namespace exageostat::prediction { * @return void */ static void PickRandomPoints(Configurations &aConfigurations, - std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, + std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, exageostat::dataunits::Locations &aMissLocation, exageostat::dataunits::Locations &aObsLocation, const int &aP); diff --git a/inst/include/results/Results.hpp b/inst/include/results/Results.hpp index 3da9e3a9..08dd54cc 100644 --- a/inst/include/results/Results.hpp +++ b/inst/include/results/Results.hpp @@ -95,6 +95,48 @@ namespace exageostat::results { */ [[nodiscard]] double GetTotalModelingExecutionTime() const; + /** + * @brief Get the MLOE. + * @return The MLOE. + */ + [[nodiscard]] double GetMLOE() const; + + /** + * @brief Get the MSPEError. + * @return The MSPEError. + */ + [[nodiscard]] double GetMSPEError() const; + + /** + * @brief Get the IDW error. + * @return The the IDW error vector. + */ + [[nodiscard]] std::vector GetIDWError() const; + + /** + * @brief Get the MMOM. + * @return The MMOM. + */ + [[nodiscard]] double GetMMOM() const; + + /** + * @brief Get the Fisher matrix element 00. + * @return the Fisher matrix element 00. + */ + [[nodiscard]] double GetFisher00() const; + + /** + * @brief Get the Fisher matrix element 11. + * @return the Fisher matrix element 11. + */ + [[nodiscard]] double GetFisher11() const; + + /** + * @brief Get the Fisher matrix element 22. + * @return the Fisher matrix element 22. + */ + [[nodiscard]] double GetFisher22() const; + /** * @brief Set the total modeling FLOPs. * @param[in] aTime The total number of FLOPs for data modeling. diff --git a/inst/include/utilities/EnumStringParser.hpp b/inst/include/utilities/EnumStringParser.hpp index 69ddfe0d..3f2746a9 100644 --- a/inst/include/utilities/EnumStringParser.hpp +++ b/inst/include/utilities/EnumStringParser.hpp @@ -51,11 +51,11 @@ inline Dimension GetInputDimension(std::string aDimension) { std::transform(aDimension.begin(), aDimension.end(), aDimension.begin(), ::tolower); - if (aDimension == "2D" || aDimension == "2d") { + if (aDimension == "2d") { return Dimension2D; - } else if (aDimension == "3D" || aDimension == "3d") { + } else if (aDimension == "3d") { return Dimension3D; - } else if (aDimension == "ST" || aDimension == "st") { + } else if (aDimension == "st") { return DimensionST; } else { const std::string msg = "Error in Initialization : Unknown computation Value" + std::string(aDimension); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13d34380..3d5c353f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,19 +11,20 @@ # @date 2024-02-04 # Add subdirectories for configurations, data-generators, data-units, and linear-algebra-solvers. -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/configurations) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-generators) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-units) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/linear-algebra-solvers) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/kernels) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/api) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/helpers) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/hardware) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/prediction) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/results) +add_subdirectory(configurations) +add_subdirectory(data-generators) +add_subdirectory(data-units) +add_subdirectory(linear-algebra-solvers) +add_subdirectory(kernels) +add_subdirectory(api) +add_subdirectory(helpers) +add_subdirectory(hardware) +add_subdirectory(prediction) +add_subdirectory(results) +add_subdirectory(data-loader) if (USE_R) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Rcpp-adapters) + add_subdirectory(Rcpp-adapters) endif () # Set the name of the library to be created. diff --git a/src/Rcpp-adapters/FunctionsAdapter.cpp b/src/Rcpp-adapters/FunctionsAdapter.cpp index 5bb330d8..dc025e6f 100644 --- a/src/Rcpp-adapters/FunctionsAdapter.cpp +++ b/src/Rcpp-adapters/FunctionsAdapter.cpp @@ -26,12 +26,12 @@ namespace exageostat::adapters { Configurations * R_InitializeArguments(const int &aProblemSize, const string &aKernelName, const vector &aTileSize, - const vector &aP_QGrid, const int &aTimeSlot, const string &aComputation, - const string &aPrecision, const vector &aCoresGPUsNumber, const int &aBand, - const int &aMaxRank, const vector &aInitialTheta, - const vector > &aLowerUpperBounds, const vector &aEstimatedTheta, - const string &aVerbose, const string &aDimension, const int &aMaxMleIterations, - const double &aTolerance, const vector &aPrediction) { + const vector &aP_QGrid, const int &aTimeSlot, const string &aComputation, + const string &aPrecision, const vector &aCoresGPUsNumber, const int &aBand, + const int &aMaxRank, const vector &aInitialTheta, + const vector > &aLowerUpperBounds, const vector &aEstimatedTheta, + const string &aVerbose, const string &aDimension, const int &aMaxMleIterations, + const double &aTolerance, const vector &aPrediction) { vector argStrings; @@ -52,13 +52,15 @@ namespace exageostat::adapters { argStrings.push_back("--band=" + to_string(aBand)); argStrings.push_back("--max_rank=" + to_string(aMaxRank)); argStrings.push_back("--iTheta=" + to_string(aInitialTheta[0]) + ":" + to_string(aInitialTheta[1]) + ":" + - to_string(aInitialTheta[2])); - argStrings.push_back("--lb=" + to_string(aLowerUpperBounds[0][0]) + ":" + to_string(aLowerUpperBounds[0][1]) + ":" + - to_string(aLowerUpperBounds[0][2])); - argStrings.push_back("--ub=" + to_string(aLowerUpperBounds[1][0]) + ":" + to_string(aLowerUpperBounds[1][1]) + ":" + - to_string(aLowerUpperBounds[1][2])); + to_string(aInitialTheta[2])); + argStrings.push_back( + "--lb=" + to_string(aLowerUpperBounds[0][0]) + ":" + to_string(aLowerUpperBounds[0][1]) + ":" + + to_string(aLowerUpperBounds[0][2])); + argStrings.push_back( + "--ub=" + to_string(aLowerUpperBounds[1][0]) + ":" + to_string(aLowerUpperBounds[1][1]) + ":" + + to_string(aLowerUpperBounds[1][2])); argStrings.push_back("--eTheta=" + to_string(aEstimatedTheta[0]) + ":" + to_string(aEstimatedTheta[1]) + ":" + - to_string(aEstimatedTheta[2])); + to_string(aEstimatedTheta[2])); argStrings.push_back("--verbose=" + aVerbose); argStrings.push_back("--dimension=" + aDimension); argStrings.push_back("--max_mle_iterations=" + to_string(aMaxMleIterations)); @@ -90,7 +92,7 @@ namespace exageostat::adapters { } // Now, allocate a char** array and copy the arguments into it. // Argv ownership is passed to Configurations, and it's responsibility for freeing it. - char **argv = new char*[argStrings.size()]; + char **argv = new char *[argStrings.size()]; for (size_t i = 0; i < argStrings.size(); ++i) { argv[i] = new char[argStrings[i].size() + 1]; // +1 for the null terminator @@ -98,23 +100,33 @@ namespace exageostat::adapters { } auto configurations = new Configurations(); - configurations->InitializeArguments(argStrings.size(), argv); + configurations->InitializeArguments(argStrings.size(), argv, true); return configurations; } - void R_ExaGeoStatAPI(ExaGeoStatHardware *apHardware, Configurations *apConfigurations) { + ExaGeoStatData *R_ExaGeoStatLoadData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData) { - if(apConfigurations->GetPrecision() == exageostat::common::DOUBLE){ - std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(*apHardware, *apConfigurations, data); - exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(*apHardware, *apConfigurations, data); - exageostat::api::ExaGeoStat::ExaGeoStatPrediction(*apHardware, *apConfigurations, data); - } - else if(apConfigurations->GetPrecision() == exageostat::common::SINGLE){ - std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(*apHardware, *apConfigurations, data); - exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(*apHardware, *apConfigurations, data); - exageostat::api::ExaGeoStat::ExaGeoStatPrediction(*apHardware, *apConfigurations, data); - } + // Wrap the raw pointer in a unique_ptr + std::unique_ptr> apDataPtr(apData); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(*apHardware, *apConfigurations, apDataPtr); + + // Since ExaGeoStatLoadData takes a reference, apDataPtr still owns the object. + // We can safely return the raw pointer, but we need to release it from apDataPtr to avoid deletion. + return apDataPtr.release(); + } + + ExaGeoStatData *R_ExaGeoStatModelData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData) { + std::unique_ptr> apDataPtr(apData); + exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(*apHardware, *apConfigurations, apDataPtr); + return apDataPtr.release(); + } + + ExaGeoStatData *R_ExaGeoStatPredictData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData) { + std::unique_ptr> apDataPtr(apData); + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(*apHardware, *apConfigurations, apDataPtr); + return apDataPtr.release(); } } \ No newline at end of file diff --git a/src/Rcpp-adapters/RcppModules.cpp b/src/Rcpp-adapters/RcppModules.cpp index 9732a0f5..d7636283 100644 --- a/src/Rcpp-adapters/RcppModules.cpp +++ b/src/Rcpp-adapters/RcppModules.cpp @@ -17,13 +17,14 @@ #include #include #include +#include /** Expose C++ class to R to be able to use Wrap and As * Allows C++ to Send and Receive Class object from R **/ - RCPP_EXPOSED_CLASS(ExaGeoStatHardware) RCPP_EXPOSED_CLASS(Configurations) +RCPP_EXPOSED_CLASS_NODECL(ExaGeoStatData) /** Expose C++ Object With the Given functions **/ RCPP_MODULE(ExaGeoStatCPP) { @@ -33,19 +34,26 @@ RCPP_MODULE(ExaGeoStatCPP) { /** Hardware Class **/ class_("Hardware") - .constructor(); + .constructor() + .method("finalize_hardware", &ExaGeoStatHardware::FinalizeHardware, "Manually finalize the hardware"); /** Configurations Class **/ class_("Configurations") .constructor(); + /** Data Class **/ + class_>("Data") + .constructor(); + /** Configurations Function **/ function("configurations_init", &exageostat::adapters::R_InitializeArguments, List::create(_["n"], _["kernel"], _["tile_size"], _["p_q"] = IntegerVector::create(1, 1), _["time_slot"] = 1, _["computation"] = "exact", _["precision"] = "double", _["cores_gpus"] = IntegerVector::create(1, 0), - _["band"] = 0, _["max_rank"] = 1, _["iTheta"], _["lb_ub"], + _["band"] = 1, _["max_rank"] = 500, _["iTheta"], _["lb_ub"], _["eTheta"]=IntegerVector::create(-1,-1,-1), _["verbose"] = "standard", _["dimension"] = "2D", - _["mle_itr"], _["tol"] = 4, _["prediction"] = IntegerVector::create(0, 0, 0, 0, 0))); + _["mle_itr"] = 0, _["tol"] = 4, _["prediction"] = IntegerVector::create(0, 0, 0, 0, 0))); - function("exageostat", &exageostat::adapters::R_ExaGeoStatAPI, List::create(_["hardware"], _["config"])); + function("simulate_data", &exageostat::adapters::R_ExaGeoStatLoadData, List::create(_["hardware"], _["config"], _["data"])); + function("model_data", &exageostat::adapters::R_ExaGeoStatModelData, List::create(_["hardware"], _["config"], _["data"])); + function("predict_data", &exageostat::adapters::R_ExaGeoStatPredictData, List::create(_["hardware"], _["config"], _["data"])); } diff --git a/src/api/ExaGeoStat.cpp b/src/api/ExaGeoStat.cpp index 2d4cbb76..6106a4ff 100644 --- a/src/api/ExaGeoStat.cpp +++ b/src/api/ExaGeoStat.cpp @@ -27,7 +27,7 @@ using namespace exageostat::prediction; template void ExaGeoStat::ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - std::unique_ptr> &aData) { + std::unique_ptr> &aData) { LOGGER("** ExaGeoStat data generation **") // Register and create a kernel object @@ -43,7 +43,7 @@ void ExaGeoStat::ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, template T ExaGeoStat::ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - std::unique_ptr> &aData, T *apMeasurementsMatrix) { + std::unique_ptr> &aData, T *apMeasurementsMatrix) { LOGGER("** ExaGeoStat data Modeling **") // Register and create a kernel object @@ -97,12 +97,11 @@ void ExaGeoStat::ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, Co T *apMeasurementsMatrix) { LOGGER("** ExaGeoStat data Prediction **") - Prediction predictor; // Register and create a kernel object kernels::Kernel *pKernel = plugins::PluginRegistry>::Create(aConfigurations.GetKernelName(), aConfigurations.GetTimeSlot()); // Add the data prediction arguments. aConfigurations.InitializeDataPredictionArguments(); - predictor.PredictMissingData(aHardware, aData, aConfigurations, apMeasurementsMatrix, *pKernel); + Prediction::PredictMissingData(aHardware, aData, aConfigurations, apMeasurementsMatrix, *pKernel); delete pKernel; } \ No newline at end of file diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index 6ab6d4b8..79c20980 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -22,6 +22,7 @@ using namespace exageostat::common; Verbose Configurations::mVerbosity = Verbose::STANDARD_MODE; bool Configurations::mIsThetaInit = false; +bool Configurations::mHeapAllocated = false; Configurations::Configurations() { @@ -64,12 +65,15 @@ Configurations::Configurations() { SetDistanceMetric(EUCLIDEAN_DISTANCE); SetAccuracy(0); SetIsNonGaussian(false); + mIsThetaInit = false; } -void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { +void Configurations::InitializeArguments(const int &aArgC, char **apArgV, const bool &aEnableR) { this->mArgC = aArgC; this->mpArgV = apArgV; + mHeapAllocated = aEnableR; + // Get the example name string example_name = apArgV[0]; // Remove the './' @@ -691,3 +695,13 @@ int Configurations::CalculateZObsNumber() { return (this->GetProblemSize()) - this->GetUnknownObservationsNb(); } +Configurations::~Configurations() { + if (mHeapAllocated) { + for (size_t i = 0; i < this->mArgC; ++i) { + delete[] this->mpArgV[i]; // Delete each string + } + delete[] this->mpArgV; // Delete the array of pointers + } + this->mpArgV = nullptr; +} + diff --git a/src/data-generators/CMakeLists.txt b/src/data-generators/CMakeLists.txt index 47cc703f..b5669976 100644 --- a/src/data-generators/CMakeLists.txt +++ b/src/data-generators/CMakeLists.txt @@ -8,7 +8,7 @@ # @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah -# @date 2023-02-14 +# @date 2024-02-04 # Add subdirectories for concrete implementations add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/concrete) @@ -16,6 +16,7 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/concrete) # Add source files to the parent scope set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/DataGenerator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/LocationGenerator.cpp ${SOURCES} PARENT_SCOPE ) diff --git a/src/data-generators/DataGenerator.cpp b/src/data-generators/DataGenerator.cpp index a667a221..342a95b5 100644 --- a/src/data-generators/DataGenerator.cpp +++ b/src/data-generators/DataGenerator.cpp @@ -13,37 +13,45 @@ #include #include -#include +#include #include using namespace exageostat::generators; +using namespace exageostat::dataLoader::csv; using namespace exageostat::generators::synthetic; -using namespace exageostat::generators::csv; using namespace exageostat::dataunits; +using namespace exageostat::common; template std::unique_ptr> DataGenerator::CreateGenerator(Configurations &apConfigurations) { + //// TODO: In case of other file support, Then we can create another layer for the factory creation depending on the file size. // Check the used Data generation method, whether it's synthetic or real. - mIsSynthetic = apConfigurations.GetIsSynthetic(); - results::Results::GetInstance()->SetIsSynthetic(mIsSynthetic); + aDataSourceType = apConfigurations.GetIsSynthetic() ? SYNTHETIC : CSV_FILE; // Return DataGenerator unique pointer of Synthetic type - if (mIsSynthetic) { + if (aDataSourceType == SYNTHETIC) { + results::Results::GetInstance()->SetIsSynthetic(true); return std::unique_ptr>(SyntheticGenerator::GetInstance()); + } else if (aDataSourceType == CSV_FILE) { + results::Results::GetInstance()->SetIsSynthetic(false); + return std::unique_ptr>(CSVLoader::GetInstance()); } else { - return std::unique_ptr>(CSVDataGenerator::GetInstance()); + throw std::runtime_error("Data Loading for this file type is unsupported for now"); } } template DataGenerator::~DataGenerator() { // Return DataGenerator unique pointer of Synthetic type - if (mIsSynthetic) { + if (aDataSourceType == SYNTHETIC) { SyntheticGenerator::GetInstance()->ReleaseInstance(); + } else if (aDataSourceType == CSV_FILE) { + CSVLoader::GetInstance()->ReleaseInstance(); } else { - CSVDataGenerator::GetInstance()->ReleaseInstance(); + std::cerr << "Data Loading for this file type is unsupported for now" << std::endl; + std::exit(1); } } -template bool DataGenerator::mIsSynthetic = true; +template DataSourceType DataGenerator::aDataSourceType = SYNTHETIC; diff --git a/src/data-generators/LocationGenerator.cpp b/src/data-generators/LocationGenerator.cpp new file mode 100644 index 00000000..e1c25b8e --- /dev/null +++ b/src/data-generators/LocationGenerator.cpp @@ -0,0 +1,122 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file LocationGenerator.cpp + * @brief Generates and manages spatial locations for ExaGeoStat. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-04 +**/ + +#include +#include + +#include +#include + +using namespace exageostat::generators; +using namespace exageostat::common; +using namespace exageostat::dataunits; +using namespace exageostat::helpers; + +template void LocationGenerator::GenerateLocations(const int &aN, const int &aTimeSlot, const Dimension &aDimension, Locations &aLocations) { + + aLocations.SetSize(aN); + int index = 0; + aLocations.SetDimension(aDimension); + + int rootN; + if (aDimension == Dimension3D) { + //Cubic root. + rootN = ceil(cbrt(aN)); + } else { + //Square root. + rootN = ceil(sqrt(aN)); + } + + int *grid = new int[rootN](); + for (auto i = 0; i < rootN; i++) { + grid[i] = i + 1; + } + T range_low = -0.4, range_high = 0.4; + + for (auto i = 0; i < rootN && index < aN; i++) { + for (auto j = 0; j < rootN && index < aN; j++) { + if (aDimension == Dimension3D) { + for (auto k = 0; k < rootN && index < aN; k++) { + aLocations.GetLocationX()[index] = + (grid[i] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; + aLocations.GetLocationY()[index] = + (grid[j] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; + aLocations.GetLocationZ()[index] = + (grid[k] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; + index++; + } + } else { + aLocations.GetLocationX()[index] = + (grid[i] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; + aLocations.GetLocationY()[index] = + (grid[j] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; + if (aDimension == DimensionST) { + aLocations.GetLocationZ()[index] = 1.0; + } + index++; + } + } + } + delete[] grid; + if (aDimension != DimensionST) { + SortLocations(aN, aDimension, aLocations); + } else { + for (auto i = 0; i < aN; i++) { + aLocations.GetLocationX()[i] = aLocations.GetLocationX()[i]; + aLocations.GetLocationY()[i] = aLocations.GetLocationY()[i]; + aLocations.GetLocationZ()[i] = (T) (i / aTimeSlot + 1); + } + } +} + +template +T LocationGenerator::UniformDistribution(const T &aRangeLow, const T &aRangeHigh) { + T myRand = (T) rand() / (T) (1.0 + RAND_MAX); + T range = aRangeHigh - aRangeLow; + return (myRand * range) + aRangeLow; +} + +template +void +LocationGenerator::SortLocations(const int &aN, const Dimension &aDimension, Locations &aLocations) { + + // Some sorting, required by spatial statistics code + uint16_t x, y, z; + uint64_t vectorZ[aN]; + + // Encode data into vector z + for (auto i = 0; i < aN; i++) { + x = (uint16_t)(aLocations.GetLocationX()[i] * (double) UINT16_MAX + .5); + y = (uint16_t)(aLocations.GetLocationY()[i] * (double) UINT16_MAX + .5); + if (aDimension != Dimension2D) { + z = (uint16_t)(aLocations.GetLocationZ()[i] * (double) UINT16_MAX + .5); + } else { + z = (uint16_t) 0.0; + } + vectorZ[i] = (SpreadBits(z) << 2) + (SpreadBits(y) << 1) + SpreadBits(x); + } + // Sort vector z + std::sort(vectorZ, vectorZ + aN, CompareUint64); + + // Decode data from vector z + for (auto i = 0; i < aN; i++) { + x = ReverseSpreadBits(vectorZ[i] >> 0); + y = ReverseSpreadBits(vectorZ[i] >> 1); + z = ReverseSpreadBits(vectorZ[i] >> 2); + aLocations.GetLocationX()[i] = (double) x / (double) UINT16_MAX; + aLocations.GetLocationY()[i] = (double) y / (double) UINT16_MAX; + if (aDimension == Dimension3D) { + aLocations.GetLocationZ()[i] = (double) z / (double) UINT16_MAX; + } + } +} \ No newline at end of file diff --git a/src/data-generators/concrete/CMakeLists.txt b/src/data-generators/concrete/CMakeLists.txt index c10d57bf..d80f538e 100644 --- a/src/data-generators/concrete/CMakeLists.txt +++ b/src/data-generators/concrete/CMakeLists.txt @@ -13,8 +13,6 @@ # Add source files to the parent scope set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/SyntheticGenerator.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/CSVDataGenerator.cpp - ${SOURCES} PARENT_SCOPE ) \ No newline at end of file diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index abd15eba..dab41214 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -13,13 +13,11 @@ **/ #include +#include using namespace exageostat::generators::synthetic; using namespace exageostat::dataunits; using namespace exageostat::common; -using namespace exageostat::kernels; -using namespace exageostat::helpers; -using namespace exageostat::linearAlgebra; template SyntheticGenerator *SyntheticGenerator::GetInstance() { @@ -48,11 +46,11 @@ SyntheticGenerator::CreateData(Configurations &aConfigurations, aConfigurations.SetInitialTheta(aConfigurations.GetInitialTheta()); // Generate Locations phase - GenerateLocations(n, aConfigurations.GetTimeSlot(), aConfigurations.GetDimension(), *locations); + LocationGenerator::GenerateLocations(n, aConfigurations.GetTimeSlot(), aConfigurations.GetDimension(), *locations); data->SetLocations(*locations); // Generate Descriptors phase - auto linear_algebra_solver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); + auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); linear_algebra_solver->GenerateSyntheticData(aConfigurations, aHardware, data, aKernel); if (aConfigurations.GetLogger()) { @@ -64,7 +62,7 @@ SyntheticGenerator::CreateData(Configurations &aConfigurations, CHAMELEON_Desc2Lap(ChamUpperLower, data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc, pMatrix, aConfigurations.GetProblemSize()); if ( CHAMELEON_Comm_rank == 0 ){ - DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + helpers::DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), aConfigurations.GetProblemSize(), aConfigurations.GetVariablesNumber(), path, *data->GetLocations()); @@ -72,7 +70,7 @@ SyntheticGenerator::CreateData(Configurations &aConfigurations, delete[] pMatrix; #else std::string path = aConfigurations.GetLoggerPath(); - DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + helpers::DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), aConfigurations.GetProblemSize(), aKernel.GetVariablesNumber(), path, *data->GetLocations()); @@ -86,142 +84,6 @@ SyntheticGenerator::CreateData(Configurations &aConfigurations, return data; } -template -void SyntheticGenerator::GenerateLocations(const int &aN, const int &aTimeSlot, const Dimension &aDimension, - Locations &aLocations) { - - aLocations.SetSize(aN); - int index = 0; - aLocations.SetDimension(aDimension); - - int rootN; - if (aDimension == Dimension3D) { - //Cubic root. - rootN = ceil(cbrt(aN)); - } else { - //Square root. - rootN = ceil(sqrt(aN)); - } - - int *grid = new int[rootN](); - for (auto i = 0; i < rootN; i++) { - grid[i] = i + 1; - } - - T range_low = -0.4, range_high = 0.4; - - for (auto i = 0; i < rootN && index < aN; i++) { - for (auto j = 0; j < rootN && index < aN; j++) { - if (aDimension == Dimension3D) { - for (auto k = 0; k < rootN && index < aN; k++) { - aLocations.GetLocationX()[index] = - (grid[i] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; - aLocations.GetLocationY()[index] = - (grid[j] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; - aLocations.GetLocationZ()[index] = - (grid[k] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; - index++; - } - } else { - aLocations.GetLocationX()[index] = (grid[i] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; - aLocations.GetLocationY()[index] = (grid[j] - 0.5 + UniformDistribution(range_low, range_high)) / rootN; - if (aDimension == DimensionST) { - aLocations.GetLocationZ()[index] = 1.0; - } - index++; - } - } - } - delete[] grid; - if (aDimension != DimensionST) { - SortLocations(aN, aDimension, aLocations); - } else { - for (auto i = 0; i < aN; i++) { - aLocations.GetLocationX()[i] = aLocations.GetLocationX()[i]; - aLocations.GetLocationY()[i] = aLocations.GetLocationY()[i]; - aLocations.GetLocationZ()[i] = (T) (i / aTimeSlot + 1); - } - } -} - -template -T SyntheticGenerator::UniformDistribution(const T &aRangeLow, const T &aRangeHigh) { - T myRand = (T) rand() / (T) (1.0 + RAND_MAX); - T range = aRangeHigh - aRangeLow; - return (myRand * range) + aRangeLow; -} - -template -void SyntheticGenerator::SortLocations(const int &aN, const Dimension &aDimension, Locations &aLocations) { - - // Some sorting, required by spatial statistics code - uint16_t x, y, z; - uint64_t vectorZ[aN]; - - // Encode data into vector z - for (auto i = 0; i < aN; i++) { - x = (uint16_t) (aLocations.GetLocationX()[i] * (double) UINT16_MAX + .5); - y = (uint16_t) (aLocations.GetLocationY()[i] * (double) UINT16_MAX + .5); - if (aDimension != Dimension2D) { - z = (uint16_t) (aLocations.GetLocationZ()[i] * (double) UINT16_MAX + .5); - } else { - z = (uint16_t) 0.0; - } - vectorZ[i] = (SpreadBits(z) << 2) + (SpreadBits(y) << 1) + SpreadBits(x); - } - // Sort vector z - std::sort(vectorZ, vectorZ + aN, CompareUint64); - - // Decode data from vector z - for (auto i = 0; i < aN; i++) { - x = ReverseSpreadBits(vectorZ[i] >> 0); - y = ReverseSpreadBits(vectorZ[i] >> 1); - z = ReverseSpreadBits(vectorZ[i] >> 2); - aLocations.GetLocationX()[i] = (double) x / (double) UINT16_MAX; - aLocations.GetLocationY()[i] = (double) y / (double) UINT16_MAX; - if (aDimension == Dimension3D) { - aLocations.GetLocationZ()[i] = (double) z / (double) UINT16_MAX; - } - } -} - -template -uint64_t SyntheticGenerator::SpreadBits(uint64_t aInputByte) { - aInputByte &= 0x000000000000ffff; - // aInputByte = ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- fedc ba98 7654 3210 - aInputByte = (aInputByte ^ (aInputByte << 24)) & 0x000000ff000000ff; - // aInputByte = ---- ---- ---- ---- ---- ---- fedc ba98 ---- ---- ---- ---- ---- ---- 7654 3210 - aInputByte = (aInputByte ^ (aInputByte << 12)) & 0x000f000f000f000f; //000 7000f000f000f - // aInputByte = ---- ---- ---- fedc ---- ---- ---- ba98 ---- ---- ---- 7654 ---- ---- ---- 3210 - aInputByte = (aInputByte ^ (aInputByte << 6)) & 0x0303030303030303; //0 0001 0 0011 0 0011 0 0011 0 - // aInputByte = ---- --fe ---- --dc ---- --ba ---- --98 ---- --76 ---- --54 ---- --32 ---- --10 - aInputByte = (aInputByte ^ (aInputByte << 3)) & 0x1111111111111111; - // aInputByte = ---f ---e ---d ---c ---b ---a ---9 ---8 ---7 ---6 ---5 ---4 ---3 ---2 ---1 ---0 - return aInputByte; -} - -template -uint64_t SyntheticGenerator::ReverseSpreadBits(uint64_t aInputByte) { - - aInputByte &= 0x1111111111111111; - // aInputByte = ---f ---e ---d ---c ---b ---a ---9 ---8 ---7 ---6 ---5 ---4 ---3 ---2 ---1 ---0 - aInputByte = (aInputByte ^ (aInputByte >> 3)) & 0x0303030303030303; - // aInputByte = ---- --fe ---- --dc ---- --ba ---- --98 ---- --76 ---- --54 ---- --32 ---- --10 - aInputByte = (aInputByte ^ (aInputByte >> 6)) & 0x000f000f000f000f; - // aInputByte = ---- ---- ---- fedc ---- ---- ---- ba98 ---- ---- ---- 7654 ---- ---- ---- 3210 - aInputByte = (aInputByte ^ (aInputByte >> 12)) & 0x000000ff000000ff; - // aInputByte = ---- ---- ---- ---- ---- ---- fedc ba98 ---- ---- ---- ---- ---- ---- 7654 3210 - aInputByte = (aInputByte ^ (aInputByte >> 24)) & 0x000000000000ffff; - // aInputByte = ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- fedc ba98 7654 3210 - return aInputByte; - -} - -template -bool SyntheticGenerator::CompareUint64(const uint64_t &aFirstValue, const uint64_t &aSecondValue) { - return aFirstValue < aSecondValue; -} - template void SyntheticGenerator::ReleaseInstance() { if (mpInstance != nullptr) { diff --git a/src/data-loader/CMakeLists.txt b/src/data-loader/CMakeLists.txt new file mode 100644 index 00000000..1c5d2e4c --- /dev/null +++ b/src/data-loader/CMakeLists.txt @@ -0,0 +1,20 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @brief CMake configuration file for Data loader module +# @version 1.0.0 +# @author Mahmoud ElKarargy +# @date 2023-02-14 + +# Add subdirectories for concrete implementations +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/concrete) + +# Add source files to the parent scope +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/DataLoader.cpp + ${SOURCES} + PARENT_SCOPE + ) diff --git a/src/data-loader/DataLoader.cpp b/src/data-loader/DataLoader.cpp new file mode 100644 index 00000000..d4da658f --- /dev/null +++ b/src/data-loader/DataLoader.cpp @@ -0,0 +1,70 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file SyntheticGenerator.cpp + * @brief Implementation of the SyntheticGenerator class + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2023-02-14 +**/ + +#include + +using namespace std; + +using namespace exageostat::dataLoader; +using namespace exageostat::dataunits; +using namespace exageostat::common; + +template +std::unique_ptr> +DataLoader::CreateData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, + exageostat::kernels::Kernel &aKernel) { + + // create vectors that will be populated with read data. + vector measurements_vector; + vector x_locations; + vector y_locations; + vector z_locations; + + aKernel.SetPValue(aConfigurations.GetTimeSlot()); + int p = aKernel.GetVariablesNumber(); + + //Read the data out of the CSV file. + this->ReadData(aConfigurations, measurements_vector, x_locations, y_locations, z_locations, p); + + //create data object + auto data = std::make_unique>(aConfigurations.GetProblemSize() / p, + aConfigurations.GetDimension()); + + //Initialize the descriptors. + auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); + linear_algebra_solver->SetContext(aHardware.GetChameleonContext()); + linear_algebra_solver->InitiateDescriptors(aConfigurations, *data->GetDescriptorData(), p); + linear_algebra_solver->ExaGeoStatLaSetTile(EXAGEOSTAT_UPPER_LOWER, 0, 0, + data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_C).chameleon_desc); + //populate data object with read data + for (int i = 0; i < aConfigurations.GetProblemSize() / p; i++) { + data->GetLocations()->GetLocationX()[i] = x_locations[i]; + data->GetLocations()->GetLocationY()[i] = y_locations[i]; + if (aConfigurations.GetDimension() != Dimension2D) { + data->GetLocations()->GetLocationZ()[i] = z_locations[i]; + } + } + for (int i = 0; i < aConfigurations.GetProblemSize(); i++) { + ((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_Z).chameleon_desc->mat)[i] = measurements_vector[i]; + } + + results::Results::GetInstance()->SetGeneratedLocationsNumber(aConfigurations.GetProblemSize() / p); + results::Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); + results::Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); + + return data; +} diff --git a/src/data-loader/concrete/CMakeLists.txt b/src/data-loader/concrete/CMakeLists.txt new file mode 100644 index 00000000..72b458bd --- /dev/null +++ b/src/data-loader/concrete/CMakeLists.txt @@ -0,0 +1,17 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @brief CMake configuration file for the data loader module +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2023-02-14 + +# Add source files to the parent scope +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/CSVLoader.cpp + ${SOURCES} + PARENT_SCOPE + ) \ No newline at end of file diff --git a/src/data-generators/concrete/CSVDataGenerator.cpp b/src/data-loader/concrete/CSVLoader.cpp similarity index 60% rename from src/data-generators/concrete/CSVDataGenerator.cpp rename to src/data-loader/concrete/CSVLoader.cpp index 7543d5bf..a8e07756 100644 --- a/src/data-generators/concrete/CSVDataGenerator.cpp +++ b/src/data-loader/concrete/CSVLoader.cpp @@ -9,83 +9,33 @@ * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2024-02-04 + * @date 2023-02-14 **/ #include -#include +#include using namespace std; -using namespace exageostat::generators::csv; using namespace exageostat::dataunits; using namespace exageostat::kernels; using namespace exageostat::common; using namespace exageostat::linearAlgebra; +using namespace exageostat::dataLoader::csv; template -CSVDataGenerator *CSVDataGenerator::GetInstance() { +CSVLoader *CSVLoader::GetInstance() { if (mpInstance == nullptr) { - mpInstance = new CSVDataGenerator(); + mpInstance = new CSVLoader(); } return mpInstance; } template -unique_ptr> -CSVDataGenerator::CreateData(Configurations &aConfigurations, - const ExaGeoStatHardware &aHardware, - exageostat::kernels::Kernel &aKernel) { - - // create vectors that will be populated with read data. - vector measurements_vector; - vector x_locations; - vector y_locations; - vector z_locations; - - aKernel.SetPValue(aConfigurations.GetTimeSlot()); - int p = aKernel.GetVariablesNumber(); - - //Read the data out of the CSV file. - ReadData(aConfigurations, measurements_vector, x_locations, y_locations, z_locations, p); - - //create data object - auto data = std::make_unique>(aConfigurations.GetProblemSize() / p, - aConfigurations.GetDimension()); - - //Initialize the descriptors. - auto linear_algebra_solver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); - linear_algebra_solver->SetContext(aHardware.GetChameleonContext()); - linear_algebra_solver->InitiateDescriptors(aConfigurations, *data->GetDescriptorData(), p); - linear_algebra_solver->ExaGeoStatLaSetTile(EXAGEOSTAT_UPPER_LOWER, 0, 0, - data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_C).chameleon_desc); - //populate data object with read data - for (int i = 0; i < aConfigurations.GetProblemSize() / p; i++) { - data->GetLocations()->GetLocationX()[i] = x_locations[i]; - data->GetLocations()->GetLocationY()[i] = y_locations[i]; - if (aConfigurations.GetDimension() != Dimension2D) { - data->GetLocations()->GetLocationZ()[i] = z_locations[i]; - } - } - for (int i = 0; i < aConfigurations.GetProblemSize(); i++) { - ((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc->mat)[i] = measurements_vector[i]; - } - - results::Results::GetInstance()->SetGeneratedLocationsNumber(aConfigurations.GetProblemSize() / p); - results::Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); - results::Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); - - return data; -} - -template -void -CSVDataGenerator::ReadData(Configurations &aConfigurations, vector &aMeasurementsMatrix, vector &aXLocations, - vector &aYLocations, vector &aZLocations, const int &aP) { +void CSVLoader::ReadData(Configurations &aConfigurations, vector &aMeasurementsMatrix, vector &aXLocations, + vector &aYLocations, vector &aZLocations, const int &aP) { //Check if the user entered a valid path for the CSV file. if (aConfigurations.GetDataPath().empty()) { @@ -119,9 +69,9 @@ CSVDataGenerator::ReadData(Configurations &aConfigurations, vector &aMeasu aYLocations.push_back(stod(token)); } if (getline(iss, token, ',')) { - //If it's a 2D locations data, the last values of the lines should be the measurement values. - //If it's 3D locations data, the third value of the line should be the Z coordinate. - //If it's ST location data, the third value of the line should be the Time coordinate + //If its a 2D locations' data, the last values of the lines should be the measurement values. + //If its 3D locations' data, the third value of the line should be the Z coordinate. + //If its ST location data, the third value of the line should be the Time coordinate if (dimension == Dimension2D) { aMeasurementsMatrix.push_back(stod(token)); //if p == 2, the third and fourth values of each line are saved in the Measurements Matrix. @@ -191,10 +141,10 @@ CSVDataGenerator::ReadData(Configurations &aConfigurations, vector &aMeasu } template -void CSVDataGenerator::ReleaseInstance() { +void CSVLoader::ReleaseInstance() { if (mpInstance != nullptr) { mpInstance = nullptr; } } -template CSVDataGenerator *CSVDataGenerator::mpInstance = nullptr; +template CSVLoader *CSVLoader::mpInstance = nullptr; diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index f74d8e5c..53b3b326 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -20,91 +20,90 @@ #include #include -ExaGeoStatHardware::ExaGeoStatHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, +using namespace exageostat::common; + +ExaGeoStatHardware::ExaGeoStatHardware(const Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber) { - this->InitHardware(aComputation, aCoreNumber, aGpuNumber); + InitHardware(aComputation, aCoreNumber, aGpuNumber); } // Constructor for R ExaGeoStatHardware::ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber) { - this->InitHardware(GetInputComputation(aComputation), aCoreNumber, aGpuNumber); + InitHardware(GetInputComputation(aComputation), aCoreNumber, aGpuNumber); } -void ExaGeoStatHardware::InitHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, +void ExaGeoStatHardware::InitHardware(const Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber) { LOGGER("** Initialise ExaGeoStat hardware **") int tag_width = 31, tag_sep = 26; + // Init hardware using Chameleon - if (!this->mpChameleonContext) { + if (!mpChameleonContext) { CHAMELEON_user_tag_size(tag_width, tag_sep); CHAMELEON_Init(aCoreNumber, aGpuNumber) - this->mpChameleonContext = chameleon_context_self(); + mpChameleonContext = chameleon_context_self(); } // Init hardware using HiCMA - if (aComputation == exageostat::common::TILE_LOW_RANK) { + if (aComputation == TILE_LOW_RANK) { #ifdef USE_HICMA - if (!this->mpHicmaContext) { + if (!mpHicmaContext) { HICMA_user_tag_size(tag_width, tag_sep); HICMA_Init(aCoreNumber, aGpuNumber); - this->mpHicmaContext = hicma_context_self(); + mpHicmaContext = hicma_context_self(); } #else - throw std::runtime_error("You need to enable Hicma to use TLR computation!"); + throw std::runtime_error("You need to enable HiCMA to use TLR computation!"); #endif } exageostat::helpers::CommunicatorMPI::GetInstance()->SetHardwareInitialization(); } +void ExaGeoStatHardware::FinalizeHardware(){ + this->~ExaGeoStatHardware(); +} + ExaGeoStatHardware::~ExaGeoStatHardware() { - // finalize hardware using HiCMA // finalize hardware using Chameleon - if (!this->mpChameleonContext) { - std::cerr << "No initialized context of Chameleon, Please initialize a hardware first" << std::endl; - exit(1); - } else { + if (mpChameleonContext) { CHAMELEON_Finalize() - this->mpChameleonContext = nullptr; + mpChameleonContext = nullptr; } + // finalize hardware using HiCMA #ifdef USE_HICMA - // In case of HiCMA, It may be enabled but without initialize the hardware. aka: dense or dst computation. - if (this->mpHicmaContext) { - HICMA_Finalize(); - this->mpHicmaContext = nullptr; - } + if (mpHicmaContext) { + HICMA_Finalize(); + mpHicmaContext = nullptr; + } #endif exageostat::helpers::CommunicatorMPI::GetInstance()->RemoveHardwareInitialization(); exageostat::results::Results::GetInstance()->PrintEndSummary(); } -#ifdef USE_HICMA - -void *ExaGeoStatHardware::GetHicmaContext() const { - if (!this->mpHicmaContext) { - throw std::runtime_error("Hardware is not initialized!"); +void *ExaGeoStatHardware::GetHicmaContext() { + if (!mpHicmaContext) { + throw std::runtime_error("HiCMA Hardware is not initialized!"); } - return this->mpHicmaContext; + return mpHicmaContext; } -#endif - -void *ExaGeoStatHardware::GetChameleonContext() const { - if (!this->mpChameleonContext) { - throw std::runtime_error("Hardware is not initialized!"); +void *ExaGeoStatHardware::GetChameleonContext() { + if (!mpChameleonContext) { + throw std::runtime_error("Chameleon Hardware is not initialized!"); } - return this->mpChameleonContext; + return mpChameleonContext; } -void *ExaGeoStatHardware::GetContext(exageostat::common::Computation aComputation) const { - if (aComputation == exageostat::common::EXACT_DENSE || aComputation == exageostat::common::DIAGONAL_APPROX) { +void *ExaGeoStatHardware::GetContext(Computation aComputation) { + if (aComputation == EXACT_DENSE || aComputation == DIAGONAL_APPROX) { return GetChameleonContext(); } - if (aComputation == exageostat::common::TILE_LOW_RANK) { -#ifdef USE_HICMA + if (aComputation == TILE_LOW_RANK) { return GetHicmaContext(); -#endif } return nullptr; } +void * ExaGeoStatHardware::mpChameleonContext = nullptr; +void * ExaGeoStatHardware::mpHicmaContext = nullptr; \ No newline at end of file diff --git a/src/helpers/BasselFunction.cpp b/src/helpers/BasselFunction.cpp new file mode 100644 index 00000000..55634bd4 --- /dev/null +++ b/src/helpers/BasselFunction.cpp @@ -0,0 +1,44 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file BasselFunction + * @brief This file contains the BasselFunction class which provides methods for computing derivatives of the modified Bessel function of the second kind. These functions are crucial in statistical and mathematical computations, especially in fields such as geostatistics and spatial analysis. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-01-24 +**/ + +extern "C" { +#include +} + +#include + +using namespace exageostat::helpers; + +template +T BasselFunction::CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue) { + if (aOrder == 0) { + return 0; + } else { + // Use a small step size to calculate the derivative numerically + const T step_size = 0.000000001; + return (gsl_sf_bessel_Knu(aOrder + step_size, aInputValue) - gsl_sf_bessel_Knu(aOrder, aInputValue)) / + step_size; + } +} + +template +T BasselFunction::CalculateSecondDerivativeBesselNu(const T &aOrder, const T &aInputValue) { + return (-0.5 * (CalculateSecondDerivativeBesselNuInput(aOrder - 1, aInputValue) + + CalculateSecondDerivativeBesselNuInput(aOrder + 1, aInputValue))); +} + +template +T BasselFunction::CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aInputValue) { + return (aOrder / aInputValue * gsl_sf_bessel_Knu(aOrder, aInputValue) - gsl_sf_bessel_Knu(aOrder + 1, aInputValue)); +} diff --git a/src/helpers/ByteHandler.cpp b/src/helpers/ByteHandler.cpp new file mode 100644 index 00000000..6f848487 --- /dev/null +++ b/src/helpers/ByteHandler.cpp @@ -0,0 +1,52 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ByteHandler.hpp + * @brief Implementation of byte manipulation functions for ExaGeoStat. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-01-24 +**/ + +#include + +namespace exageostat::helpers { + + uint64_t SpreadBits(uint64_t aInputByte) { + + aInputByte &= 0x000000000000ffff; + // aInputByte = ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- fedc ba98 7654 3210 + aInputByte = (aInputByte ^ (aInputByte << 24)) & 0x000000ff000000ff; + // aInputByte = ---- ---- ---- ---- ---- ---- fedc ba98 ---- ---- ---- ---- ---- ---- 7654 3210 + aInputByte = (aInputByte ^ (aInputByte << 12)) & 0x000f000f000f000f; //000 7000f000f000f + // aInputByte = ---- ---- ---- fedc ---- ---- ---- ba98 ---- ---- ---- 7654 ---- ---- ---- 3210 + aInputByte = (aInputByte ^ (aInputByte << 6)) & 0x0303030303030303; //0 0001 0 0011 0 0011 0 0011 0 + // aInputByte = ---- --fe ---- --dc ---- --ba ---- --98 ---- --76 ---- --54 ---- --32 ---- --10 + aInputByte = (aInputByte ^ (aInputByte << 3)) & 0x1111111111111111; + // aInputByte = ---f ---e ---d ---c ---b ---a ---9 ---8 ---7 ---6 ---5 ---4 ---3 ---2 ---1 ---0 + return aInputByte; + } + + uint64_t ReverseSpreadBits(uint64_t aInputByte) { + + aInputByte &= 0x1111111111111111; + // aInputByte = ---f ---e ---d ---c ---b ---a ---9 ---8 ---7 ---6 ---5 ---4 ---3 ---2 ---1 ---0 + aInputByte = (aInputByte ^ (aInputByte >> 3)) & 0x0303030303030303; + // aInputByte = ---- --fe ---- --dc ---- --ba ---- --98 ---- --76 ---- --54 ---- --32 ---- --10 + aInputByte = (aInputByte ^ (aInputByte >> 6)) & 0x000f000f000f000f; + // aInputByte = ---- ---- ---- fedc ---- ---- ---- ba98 ---- ---- ---- 7654 ---- ---- ---- 3210 + aInputByte = (aInputByte ^ (aInputByte >> 12)) & 0x000000ff000000ff; + // aInputByte = ---- ---- ---- ---- ---- ---- fedc ba98 ---- ---- ---- ---- ---- ---- 7654 3210 + aInputByte = (aInputByte ^ (aInputByte >> 24)) & 0x000000000000ffff; + // aInputByte = ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- fedc ba98 7654 3210 + return aInputByte; + } + + bool CompareUint64(const uint64_t &aFirstValue, const uint64_t &aSecondValue) { + return aFirstValue < aSecondValue; + } +} \ No newline at end of file diff --git a/src/helpers/CMakeLists.txt b/src/helpers/CMakeLists.txt index cbcb2d5c..550bc47d 100644 --- a/src/helpers/CMakeLists.txt +++ b/src/helpers/CMakeLists.txt @@ -13,6 +13,8 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/DiskWriter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DistanceCalculationHelpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CommunicatorMPI.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ByteHandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BasselFunction.cpp ${SOURCES} PARENT_SCOPE ) \ No newline at end of file diff --git a/src/helpers/DiskWriter.cpp b/src/helpers/DiskWriter.cpp index 3f83bc06..3cef39cf 100644 --- a/src/helpers/DiskWriter.cpp +++ b/src/helpers/DiskWriter.cpp @@ -12,11 +12,10 @@ * @date 2023-06-08 **/ -#include #include -#include #include + using namespace std; using namespace exageostat::helpers; @@ -111,4 +110,4 @@ void DiskWriter::WriteVectorsToDisk(const T &aMatrixPointer, const int &aProb } } p_file_synthetic.close(); -} +} \ No newline at end of file diff --git a/src/kernels/Kernel.cpp b/src/kernels/Kernel.cpp index 08ee91b8..78c992ee 100644 --- a/src/kernels/Kernel.cpp +++ b/src/kernels/Kernel.cpp @@ -12,40 +12,12 @@ * @date 2023-04-12 **/ -extern "C" { -#include -} - #include using namespace std; -using namespace exageostat::dataunits; using namespace exageostat::kernels; -template -T Kernel::CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue) { - if (aOrder == 0) { - return 0; - } else { - // Use a small step size to calculate the derivative numerically - const T step_size = 0.000000001; - return (gsl_sf_bessel_Knu(aOrder + step_size, aInputValue) - gsl_sf_bessel_Knu(aOrder, aInputValue)) / - step_size; - } -} - -template -T Kernel::CalculateSecondDerivativeBesselNu(const T &aOrder, const T &aInputValue) { - return (-0.5 * (CalculateSecondDerivativeBesselNuInput(aOrder - 1, aInputValue) + - CalculateSecondDerivativeBesselNuInput(aOrder + 1, aInputValue))); -} - -template -T Kernel::CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aInputValue) { - return (aOrder / aInputValue * gsl_sf_bessel_Knu(aOrder, aInputValue) - gsl_sf_bessel_Knu(aOrder + 1, aInputValue)); -} - template int Kernel::GetVariablesNumber() const { return this->mVariablesNumber; diff --git a/src/kernels/concrete/BivariateMaternFlexible.cpp b/src/kernels/concrete/BivariateMaternFlexible.cpp index 945da9f9..9fc50947 100644 --- a/src/kernels/concrete/BivariateMaternFlexible.cpp +++ b/src/kernels/concrete/BivariateMaternFlexible.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/BivariateMaternParsimonious.cpp b/src/kernels/concrete/BivariateMaternParsimonious.cpp index 284a0e66..1431aec7 100644 --- a/src/kernels/concrete/BivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/BivariateMaternParsimonious.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp index 8edc047c..a3a49765 100644 --- a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/TrivariateMaternParsimonious.cpp b/src/kernels/concrete/TrivariateMaternParsimonious.cpp index 8050433e..1734861f 100644 --- a/src/kernels/concrete/TrivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/TrivariateMaternParsimonious.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateExpNonGaussian.cpp b/src/kernels/concrete/UnivariateExpNonGaussian.cpp index e4a31d1b..d207cbfc 100644 --- a/src/kernels/concrete/UnivariateExpNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateExpNonGaussian.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternDbeta.cpp b/src/kernels/concrete/UnivariateMaternDbeta.cpp index 8f2cbda5..fd42dec9 100644 --- a/src/kernels/concrete/UnivariateMaternDbeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDbeta.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp index 6de1d251..c1370efa 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp index e590e59e..e85f3138 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp @@ -13,7 +13,6 @@ **/ #include -#include using namespace exageostat::kernels; using namespace exageostat::dataunits; @@ -98,7 +97,7 @@ UnivariateMaternDdbetaNu::GenerateCovarianceMatrix(T *apMatrixA, const int &a aLocalTheta[2] + 1, expr)) + pow(expr, aLocalTheta[2]) * - this->CalculateSecondDerivativeBesselNuInput( + BasselFunction::CalculateSecondDerivativeBesselNuInput( aLocalTheta[2], expr))); apMatrixA[i + j * aRowsNumber] = (-1 / aLocalTheta[1] * (con * pow(expr, aLocalTheta[2]) * gsl_sf_bessel_Knu(aLocalTheta[2], expr)) - diff --git a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp index c2352493..c1038762 100644 --- a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; @@ -72,22 +72,20 @@ UnivariateMaternDdnuNu::GenerateCovarianceMatrix(T *apMatrixA, const int &aRo (pow(expr, aLocalTheta[2]) * log(expr) * gsl_sf_bessel_Knu(aLocalTheta[2], expr) + pow(expr, aLocalTheta[2]) * - this->CalculateDerivativeBesselNu(aLocalTheta[2], + BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr))); nu_expr_dprime = (1 - aLocalTheta[2]) * 1 / pow(2, aLocalTheta[2]) * 1 / tgamma(aLocalTheta[2]) * - pow(expr, aLocalTheta[2]) * this->CalculateDerivativeBesselNu(aLocalTheta[2], expr) + + pow(expr, aLocalTheta[2]) * BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr) + pow(2, 1 - aLocalTheta[2]) * (-1 / tgamma(aLocalTheta[2]) * gsl_sf_psi(aLocalTheta[2]) * pow(expr, aLocalTheta[2]) * - this->CalculateDerivativeBesselNu(aLocalTheta[2], expr) + 1 / tgamma(aLocalTheta[2]) * + BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr) + 1 / tgamma(aLocalTheta[2]) * (pow(expr, aLocalTheta[2]) * log(expr) * - this->CalculateDerivativeBesselNu( + BasselFunction::CalculateDerivativeBesselNu( aLocalTheta[2], expr) + pow(expr, aLocalTheta[2]) * - this->CalculateSecondDerivativeBesselNu( - aLocalTheta[2], - expr))); + BasselFunction::CalculateSecondDerivativeBesselNu(aLocalTheta[2],expr))); apMatrixA[i + j * aRowsNumber] = (-0.5 * con * pow(expr, aLocalTheta[2]) * gsl_sf_bessel_Knu(aLocalTheta[2], expr) + (1 - aLocalTheta[2]) / 2 * nu_expr - diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp index 4baf4b32..f6eacdc6 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp index 5b04e2cb..1c934301 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternDnu.cpp b/src/kernels/concrete/UnivariateMaternDnu.cpp index 9a012044..e9e642ce 100644 --- a/src/kernels/concrete/UnivariateMaternDnu.cpp +++ b/src/kernels/concrete/UnivariateMaternDnu.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; @@ -68,7 +68,7 @@ void UnivariateMaternDnu::GenerateCovarianceMatrix(T *apMatrixA, const int &a (pow(expr, aLocalTheta[2]) * log(expr) * gsl_sf_bessel_Knu(aLocalTheta[2], expr) + pow(expr, aLocalTheta[2]) * - Kernel::CalculateDerivativeBesselNu(aLocalTheta[2], expr))); + BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr))); apMatrixA[i + j * aRowsNumber] = sigma_square * nu_expr; } j0++; diff --git a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp index 7ce72c82..406f00be 100644 --- a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp @@ -15,7 +15,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp index 76f5ae3b..cdb458fe 100644 --- a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp index d29ac1f2..19a43a6f 100644 --- a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateMaternStationary.cpp b/src/kernels/concrete/UnivariateMaternStationary.cpp index f878bf1e..4facc1e3 100644 --- a/src/kernels/concrete/UnivariateMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternStationary.cpp @@ -13,7 +13,7 @@ **/ #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp index a789a56a..b7a7eb32 100644 --- a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp @@ -17,7 +17,7 @@ #include #include -#include + using namespace exageostat::kernels; using namespace exageostat::dataunits; diff --git a/src/prediction/Prediction.cpp b/src/prediction/Prediction.cpp index b42e98b1..4a955d96 100644 --- a/src/prediction/Prediction.cpp +++ b/src/prediction/Prediction.cpp @@ -22,7 +22,7 @@ using namespace exageostat::dataunits; template void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, + std::unique_ptr> &aData, Configurations &aConfigurations, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) { @@ -35,6 +35,7 @@ void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, break; } } + if (!can_predict && (aConfigurations.GetIsMLOEMMOM() || aConfigurations.GetIsMSPE() || aConfigurations.GetIsFisher())) { throw std::runtime_error( @@ -47,7 +48,7 @@ void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, int n_z_obs = aConfigurations.CalculateZObsNumber(); auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(common::EXACT_DENSE); - //FISHER Prediction Function Call + // FISHER Prediction Function Call if (aConfigurations.GetIsFisher()) { LOGGER("---- Using Prediction Function Fisher ----") T *fisher_results; @@ -174,7 +175,7 @@ void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, template void Prediction::InitializePredictionArguments(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, std::unique_ptr> &aLinearAlgebraSolver, T *apZObs, T *apZActual, Locations &aMissLocation, Locations &aObsLocation, T *apMeasurementsMatrix, const int &aP) { diff --git a/src/results/Results.cpp b/src/results/Results.cpp index 5f831dc6..d93d6ae7 100644 --- a/src/results/Results.cpp +++ b/src/results/Results.cpp @@ -116,7 +116,7 @@ void Results::PrintEndSummary() { LOGGER("") } } - if(this->mFisher00 != 0){ + if (this->mFisher00 != 0) { LOGGER(" #Fisher") LOGGER(" #Sd For Sigma2: " << this->mFisher00) LOGGER(" #Sd For Alpha: " << this->mFisher11) @@ -242,4 +242,35 @@ void Results::SetFisher11(double aFisher11) { void Results::SetFisher22(double aFisher22) { this->mFisher22 = aFisher22; -} \ No newline at end of file +} + + +double Results::GetMLOE() const { + return this->mMLOE; +} + +double Results::GetMSPEError() const { + return this->mMSPEError; +} + +std::vector Results::GetIDWError() const { + return this->mIDWError; +} + +double Results::GetMMOM() const { + return this->mMMOM; +} + +double Results::GetFisher00() const { + return this->mFisher00; +} + + +double Results::GetFisher11() const { + return this->mFisher11; +} + +double Results::GetFisher22() const { + return this->mFisher22; +} + diff --git a/tests/R-tests/TestExaGeoStat.R b/tests/R-tests/TestExaGeoStat.R new file mode 100644 index 00000000..ba1d5ab0 --- /dev/null +++ b/tests/R-tests/TestExaGeoStat.R @@ -0,0 +1,53 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# + +# @file TestExaGeoStat.R +# @brief +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-02-09 +# + +paste("---------------------------------------------------------------") +paste("ExaGeoStat with all Modules - Dense") +library("ExaGeoStatCPP") + +ncores <- 1 +ngpus <- 0 +problem_size <- 16 +dts <- 8 +lts <- 0 +computation <- "exact" + +hardware <- new(Hardware, computation, ncores, ngpus) +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=10, prediction=c(5,1,1,1,1)) +data_source <- new(Data, problem_size, "2D") + +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) +model_data(hardware=hardware, config=config, data=exageostat_data) +predict_data(hardware=hardware, config=config, data=exageostat_data) + +paste("---------------------------------------------------------------") +hardware$finalize_hardware() +paste("ExaGeoStat with data generation and Modeling only") + +problem_size <- 10 +ncores <- 5 + +hardware <- new(Hardware, computation, ncores, ngpus) +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5) +data_source <- new(Data, problem_size, "2D") +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) +model_data(hardware=hardware, config=config, data=exageostat_data) + +paste("---------------------------------------------------------------") +paste("ExaGeoStat with data generation and Prediction using MLOE_MMOM only") + +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(1,0.1,0.3), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,0,0,1)) +data_source <- new(Data, problem_size, "2D") +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) +predict_data(hardware=hardware, config=config, data=exageostat_data) + + diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index 6d9d4430..d3170eff 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -6,14 +6,25 @@ # @file CMakeLists.txt # @version 1.1.0 # @author Mahmoud ElKarargy -# @date 2023-01-31 +# @date 2024-01-24 add_subdirectory(api) -add_subdirectory(linear-algebra-solvers) -add_subdirectory(data-generators) add_subdirectory(configurations) -add_subdirectory(kernels) +add_subdirectory(data-generators) +add_subdirectory(hardware) add_subdirectory(helpers) +add_subdirectory(kernels) +add_subdirectory(linear-algebra-solvers) +add_subdirectory(prediction) +add_subdirectory(results) + +if(USE_HICMA) + add_subdirectory(data-units) +endif () + +if (USE_R) + add_subdirectory(Rcpp-adapters) +endif () enable_testing() add_executable(exageostat-tests ${EXAGEOSTAT_TESTFILES}) diff --git a/tests/cpp-tests/Rcpp-adapters/CMakeLists.txt b/tests/cpp-tests/Rcpp-adapters/CMakeLists.txt new file mode 100644 index 00000000..1be993fa --- /dev/null +++ b/tests/cpp-tests/Rcpp-adapters/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-02-09 + +set(EXAGEOSTAT_TESTFILES + + ${CMAKE_CURRENT_SOURCE_DIR}/TestAllRFunctions.cpp + ${EXAGEOSTAT_TESTFILES} + PARENT_SCOPE + ) + diff --git a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp new file mode 100644 index 00000000..36053745 --- /dev/null +++ b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestAllRFunctions.cpp + * @brief Test suite for R/Rcpp adapters in C++. + * @details This file contains tests for R methods adapted for use in C++ within the ExaGeoStat software package. + * It includes tests for initializing arguments, loading data, modeling data, and predicting data using Rcpp adapters. + * The test suite specifically verifies the integration and functionality of R methods through the Rcpp interface + * within a C++ environment, ensuring that statistical models and algorithms are correctly initialized, + * data is accurately loaded and processed, and predictions are properly executed. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-09 +**/ + +#include + +#include + +using namespace std; + +using namespace exageostat::adapters; + +void TEST_ALL_R_METHODS() { + SECTION("R METHODS"){ + + auto configurations = R_InitializeArguments(16, "univariate_matern_stationary", {8, 0}, {1, 1}, 1, "exact", "double", {1, 0}, 0, 1, {1,0.1,0.5}, { {0.1,0.1,0.1}, {5,5,5}}, {-1,-1,-1}, "standard", "2D", 10, 4, {5,1,1,1,1}); + auto hardware = ExaGeoStatHardware("exact", 1, 0); + auto data_source = new ExaGeoStatData(10, "2D"); + auto exageostat_data = R_ExaGeoStatLoadData(&hardware, configurations, data_source); + R_ExaGeoStatModelData(&hardware, configurations, exageostat_data); + R_ExaGeoStatPredictData(&hardware, configurations, exageostat_data); + + delete exageostat_data; + delete configurations; + } +} + +TEST_CASE("Test R/Rcpp adapters in C++") { + TEST_ALL_R_METHODS(); +} \ No newline at end of file diff --git a/tests/cpp-tests/configurations/TestConfigurations.cpp b/tests/cpp-tests/configurations/TestConfigurations.cpp index fe8873db..f3b06e9a 100644 --- a/tests/cpp-tests/configurations/TestConfigurations.cpp +++ b/tests/cpp-tests/configurations/TestConfigurations.cpp @@ -24,6 +24,74 @@ using namespace std; using namespace exageostat::common; +void TEST_ARGUMENT_INITIALIZATION(){ + + const int argc = 17; + char* argv[] = { + const_cast("program_name"), + const_cast("--N=16"), + const_cast("--dts=8"), + const_cast("--kernel=univariate_matern_stationary"), + const_cast("--computation=exact"), + const_cast("--precision=double"), + const_cast("--initial_theta=1:0.1:0.5"), + const_cast("--ub=5:5:5"), + const_cast("--lb=0.1:0.1:0.1"), + const_cast("--max_mle_iterations=5"), + const_cast("--tolerance=4"), + const_cast("--ZMiss=6"), + const_cast("--mspe"), + const_cast("--idw"), + const_cast("--mloe-mmom"), + const_cast("--fisher"), + const_cast("--data_path=./dummy-path") + }; + + Configurations configurations; + + // Initialize configuration dictionary with only common arguments + configurations.InitializeArguments(argc, argv); + + REQUIRE(configurations.GetProblemSize() == 16); + REQUIRE(configurations.GetKernelName() == "UnivariateMaternStationary"); + REQUIRE(configurations.GetDenseTileSize() == 8); + REQUIRE(configurations.GetPrecision() == DOUBLE); + + // No data generation arguments initialized + REQUIRE(configurations.GetDataPath() == string("")); + + // No data modeling arguments initialized + REQUIRE_THROWS(configurations.GetMaxMleIterations()); + REQUIRE_THROWS(configurations.GetTolerance()); + + // No data prediction arguments initialized + REQUIRE(configurations.GetIsMSPE() == false); + REQUIRE(configurations.GetIsIDW() == false); + REQUIRE(configurations.GetIsFisher() == false); + REQUIRE(configurations.GetIsMLOEMMOM() == false); + REQUIRE(configurations.GetUnknownObservationsNb() == 0); + + // Data generation arguments initialized + configurations.InitializeDataGenerationArguments(); + + REQUIRE(configurations.GetDataPath() == string("./dummy-path")); + + // Data modelling arguments initialized + configurations.InitializeDataModelingArguments(); + + REQUIRE(configurations.GetMaxMleIterations() == 5); + REQUIRE(configurations.GetTolerance() == 4); + + // Data prediction arguments initialized + configurations.InitializeDataPredictionArguments(); + + REQUIRE(configurations.GetIsMSPE() == true); + REQUIRE(configurations.GetIsIDW() == true); + REQUIRE(configurations.GetIsFisher() == true); + REQUIRE(configurations.GetIsMLOEMMOM() == true); + REQUIRE(configurations.GetUnknownObservationsNb() == 6); + +} void TEST_SYNTHETIC_CONFIGURATIONS() { Configurations synthetic_data_configurations; @@ -112,7 +180,8 @@ void TEST_COPY_CONSTRUCTOR() { } } -TEST_CASE("Synthetic Data Configurations") { +TEST_CASE("Configurations Tests") { TEST_SYNTHETIC_CONFIGURATIONS(); TEST_COPY_CONSTRUCTOR(); + TEST_ARGUMENT_INITIALIZATION(); } diff --git a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp index 207e5b1c..e6d02d74 100644 --- a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include using namespace std; @@ -28,6 +30,7 @@ using namespace exageostat::generators; using namespace exageostat::dataunits; using namespace exageostat::common; using namespace exageostat::kernels; +using namespace exageostat::helpers; void TEST_SPREAD_REVERSED_BITS() { @@ -40,7 +43,7 @@ void TEST_SPREAD_REVERSED_BITS() { { uint16_t randomByte = INT16_MAX; REQUIRE(randomByte == 0x7FFF); - uint64_t returnedByte = SyntheticGenerator::SpreadBits(randomByte); + uint64_t returnedByte = SpreadBits(randomByte); // This because 7FFF will first be 16 hex = 64 bits // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 7FFF // 7FFF to bits is 0111111111111111 @@ -50,7 +53,7 @@ void TEST_SPREAD_REVERSED_BITS() { }SECTION("Reverse Spread Bytes") { uint64_t randomByte = 0x0111111111111111; - uint16_t returnedByte = SyntheticGenerator::ReverseSpreadBits(randomByte); + uint16_t returnedByte = ReverseSpreadBits(randomByte); REQUIRE(returnedByte == 0x7FFF); }SECTION("Spread & reverse 3D") { @@ -60,7 +63,7 @@ void TEST_SPREAD_REVERSED_BITS() { uint16_t z = INT16_MAX; uint64_t vectorZ; - vectorZ = (SyntheticGenerator::SpreadBits(z) << 2); + vectorZ = (SpreadBits(z) << 2); // vector Z will be // ---- ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 ---1 // After shifting by 2 @@ -69,7 +72,7 @@ void TEST_SPREAD_REVERSED_BITS() { REQUIRE(vectorZ == 0x0444444444444444); // Do the same for Y - vectorZ += (SyntheticGenerator::SpreadBits(y) << 1); + vectorZ += (SpreadBits(y) << 1); // If vector Z was empty it will be // ---- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- // But sine vectorZ is already contains Z. then by adding both we get @@ -78,16 +81,16 @@ void TEST_SPREAD_REVERSED_BITS() { REQUIRE(vectorZ == 0x0666666666666666); // Lastly, Adding X - vectorZ += SyntheticGenerator::SpreadBits(x); + vectorZ += SpreadBits(x); // Adding X without shifting will result in // ---- -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 -111 // Since 0111 is equal to 7 in hex then expected to be 0x0777777777777777 REQUIRE(vectorZ == 0x0777777777777777); // Spreading is Done, Now reversing. - uint16_t reversed_x = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 0); - uint16_t reversed_y = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 1); - uint16_t reversed_z = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 2); + uint16_t reversed_x = ReverseSpreadBits(vectorZ >> 0); + uint16_t reversed_y = ReverseSpreadBits(vectorZ >> 1); + uint16_t reversed_z = ReverseSpreadBits(vectorZ >> 2); // What we reversed is what we send. REQUIRE(reversed_x == INT16_MAX); @@ -106,13 +109,13 @@ void TEST_SPREAD_REVERSED_BITS() { uint16_t z_random = 22222; //Spreading - vectorZ = (SyntheticGenerator::SpreadBits(z_random) << 2) + - (SyntheticGenerator::SpreadBits(y_random) << 1) + - SyntheticGenerator::SpreadBits(x_random); + vectorZ = (SpreadBits(z_random) << 2) + + (SpreadBits(y_random) << 1) + + SpreadBits(x_random); // Spreading is Done, Now reversing. - uint16_t reversed_x_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 0); - uint16_t reversed_y_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 1); - uint16_t reversed_z_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 2); + uint16_t reversed_x_random = ReverseSpreadBits(vectorZ >> 0); + uint16_t reversed_y_random = ReverseSpreadBits(vectorZ >> 1); + uint16_t reversed_z_random = ReverseSpreadBits(vectorZ >> 2); REQUIRE(x_random == reversed_x_random); REQUIRE(y_random == reversed_y_random); @@ -127,28 +130,28 @@ void TEST_SPREAD_REVERSED_BITS() { uint16_t z = 0; uint64_t vectorZ; - vectorZ = (SyntheticGenerator::SpreadBits(z) << 2); + vectorZ = (SpreadBits(z) << 2); // vector Z will be zeros REQUIRE(vectorZ == 0x0000000000000000); // Do the same for Y - vectorZ += (SyntheticGenerator::SpreadBits(y) << 1); + vectorZ += (SpreadBits(y) << 1); // vector Z after shift by one will be // ---- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- --1- // Since 0010 is equal to 2 in hex then expected to be 0x022222222222222 REQUIRE(vectorZ == 0x0222222222222222); // Lastly, Adding X - vectorZ += SyntheticGenerator::SpreadBits(x); + vectorZ += SpreadBits(x); // Adding X without shifting will result in // ---- --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 --11 // Since 0011 is equal to 3 in hex then expected to be 0x0333333333333333 REQUIRE(vectorZ == 0x0333333333333333); // Spreading is Done, Now reversing. - uint16_t reversed_x = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 0); - uint16_t reversed_y = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 1); - uint16_t reversed_z = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 2); + uint16_t reversed_x = ReverseSpreadBits(vectorZ >> 0); + uint16_t reversed_y = ReverseSpreadBits(vectorZ >> 1); + uint16_t reversed_z = ReverseSpreadBits(vectorZ >> 2); // What we reversed is what we send. REQUIRE(reversed_x == INT16_MAX); @@ -167,13 +170,13 @@ void TEST_SPREAD_REVERSED_BITS() { uint16_t z_random = 0; //Spreading - vectorZ = (SyntheticGenerator::SpreadBits(z_random) << 2) + - (SyntheticGenerator::SpreadBits(y_random) << 1) + - SyntheticGenerator::SpreadBits(x_random); + vectorZ = (SpreadBits(z_random) << 2) + + (SpreadBits(y_random) << 1) + + SpreadBits(x_random); // Spreading is Done, Now reversing. - uint16_t reversed_x_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 0); - uint16_t reversed_y_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 1); - uint16_t reversed_z_random = SyntheticGenerator::ReverseSpreadBits(vectorZ >> 2); + uint16_t reversed_x_random = ReverseSpreadBits(vectorZ >> 0); + uint16_t reversed_y_random = ReverseSpreadBits(vectorZ >> 1); + uint16_t reversed_z_random = ReverseSpreadBits(vectorZ >> 2); REQUIRE(x_random == reversed_x_random); REQUIRE(y_random == reversed_y_random); @@ -267,7 +270,7 @@ void TEST_HELPERS_FUNCTIONS() { { double lowerRange = -0.4; double higherRange = 0.4; - double uniformed_num = SyntheticGenerator::UniformDistribution(lowerRange, higherRange); + double uniformed_num = LocationGenerator::UniformDistribution(lowerRange, higherRange); REQUIRE(uniformed_num > lowerRange); REQUIRE(uniformed_num < 1); } @@ -275,9 +278,9 @@ void TEST_HELPERS_FUNCTIONS() { SECTION("Compare Uint32") { uint32_t num1 = 16; - REQUIRE(SyntheticGenerator::CompareUint64(num1, num1) == false); - REQUIRE(SyntheticGenerator::CompareUint64(num1, num1 + num1) == true); - REQUIRE(SyntheticGenerator::CompareUint64(num1 + num1, num1) == false); + REQUIRE(CompareUint64(num1, num1) == false); + REQUIRE(CompareUint64(num1, num1 + num1) == true); + REQUIRE(CompareUint64(num1 + num1, num1) == false); SyntheticGenerator::ReleaseInstance(); } } diff --git a/tests/cpp-tests/data-units/CMakeLists.txt b/tests/cpp-tests/data-units/CMakeLists.txt new file mode 100644 index 00000000..e08ed3ec --- /dev/null +++ b/tests/cpp-tests/data-units/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-01-24 + +set(EXAGEOSTAT_TESTFILES + ${CMAKE_CURRENT_SOURCE_DIR}/TestDescriptorData.cpp + ${EXAGEOSTAT_TESTFILES} + PARENT_SCOPE + ) diff --git a/tests/cpp-tests/data-units/TestDescriptorData.cpp b/tests/cpp-tests/data-units/TestDescriptorData.cpp new file mode 100644 index 00000000..ef332149 --- /dev/null +++ b/tests/cpp-tests/data-units/TestDescriptorData.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestDescriptorData.cpp + * @brief Tests for CHAMELEON to HICMA descriptor conversion in ExaGeoStat. + * @details This test case verifies the conversion of matrix descriptors from the CHAMELEON format to the HICMA format. + * It ensures that key properties of the matrix descriptor, such as dimensions, block sizes, and grid distribution parameters, are preserved during the conversion process. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-04 +**/ + +#include + +#include + +using namespace std; + +using namespace exageostat::linearAlgebra; +using namespace exageostat::common; +using namespace exageostat::dataunits; + +void TEST_CHAM_TO_HICMA_CONV(){ + + // Initialize Configuration + Configurations synthetic_data_configurations; + synthetic_data_configurations.SetProblemSize(4); + synthetic_data_configurations.SetDenseTileSize(1); + + // Initialize linear algebra solver + int p = 1; + auto hardware = ExaGeoStatHardware(EXACT_DENSE, 1, 0); + auto linearAlgebraSolver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); + linearAlgebraSolver->SetContext(hardware.GetChameleonContext()); + auto *data = new DescriptorData(); + linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, p); + + // Create CHAM descriptor and convert it to HICMA descriptor + auto *CHAM_descriptorC = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; + auto *HICMA_descriptor = data->ConvertChameleonToHicma(CHAM_descriptorC); + + // Verify common attributes are of same value + REQUIRE(CHAM_descriptorC->m == HICMA_descriptor->m); + REQUIRE(CHAM_descriptorC->n == HICMA_descriptor->n); + REQUIRE(CHAM_descriptorC->mb == HICMA_descriptor->mb); + REQUIRE(CHAM_descriptorC->nb == HICMA_descriptor->nb); + REQUIRE(CHAM_descriptorC->bsiz == HICMA_descriptor->bsiz); + REQUIRE(CHAM_descriptorC->i == HICMA_descriptor->i); + REQUIRE(CHAM_descriptorC->j == HICMA_descriptor->j); + REQUIRE(CHAM_descriptorC->mt == HICMA_descriptor->mt); + REQUIRE(CHAM_descriptorC->nt == HICMA_descriptor->nt); + REQUIRE(CHAM_descriptorC->lm == HICMA_descriptor->lm); + REQUIRE(CHAM_descriptorC->ln == HICMA_descriptor->ln); + REQUIRE(CHAM_descriptorC->p == HICMA_descriptor->p); + REQUIRE(CHAM_descriptorC->q == HICMA_descriptor->q); + + delete data; +} + +TEST_CASE(" CHAMELEON To HICMA Converter"){ + TEST_CHAM_TO_HICMA_CONV(); +} diff --git a/tests/cpp-tests/hardware/CMakeLists.txt b/tests/cpp-tests/hardware/CMakeLists.txt new file mode 100644 index 00000000..370d53d7 --- /dev/null +++ b/tests/cpp-tests/hardware/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-01-24 + +set(EXAGEOSTAT_TESTFILES + ${CMAKE_CURRENT_SOURCE_DIR}/TestExaGeoStatHardware.cpp + ${EXAGEOSTAT_TESTFILES} + PARENT_SCOPE +) diff --git a/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp b/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp new file mode 100644 index 00000000..a4b50332 --- /dev/null +++ b/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp @@ -0,0 +1,111 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestExaGeoStatHardware.cpp + * @brief Unit tests for the ExaGeoStatHardware class in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the ExaGeoStatHardware class + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-24 +**/ + +#include + +#include + +using namespace exageostat::common; + +void TEST_HARDWARE_CONSTRUCTION() { + + //Initialize multiple instances of the hardware, and verify chameleon context is not null. + auto hardware_1 = ExaGeoStatHardware(EXACT_DENSE, 4, 0); + REQUIRE(hardware_1.GetChameleonContext() != nullptr); + + auto hardware_2 = ExaGeoStatHardware(DIAGONAL_APPROX, 4, 0); + REQUIRE(hardware_2.GetChameleonContext() != nullptr); + REQUIRE(hardware_1.GetChameleonContext() == hardware_2.GetChameleonContext()); + + //In case of HICMA initialize a TLR hardware and verify non-null context, + // otherwise exception is raised in case of initialization. +#ifdef USE_HICMA + auto hardware_3 = ExaGeoStatHardware(TILE_LOW_RANK, 4, 0); + REQUIRE(hardware_3.GetHicmaContext() != nullptr); + + auto hardware_4 = ExaGeoStatHardware(TILE_LOW_RANK, 4, 0); + REQUIRE(hardware_4.GetHicmaContext() != nullptr); + REQUIRE(hardware_3.GetHicmaContext() == hardware_4.GetHicmaContext()); +#else + REQUIRE_THROWS(ExaGeoStatHardware(TILE_LOW_RANK, 4, 0)); +#endif + +} + +void TEST_STATIC_CONTEXT() { + + //Initialize multiple instances of the hardware: EXACT_DENSE,DIAGONAL_APPROX ,and verify they all have same static Chameleon context. + auto hardware_1 = ExaGeoStatHardware(EXACT_DENSE, 4, 0); + auto context_hardware_1 = ExaGeoStatHardware::GetContext(EXACT_DENSE); + + auto hardware_2 = ExaGeoStatHardware(EXACT_DENSE, 1, 0); + auto context_hardware_2 = ExaGeoStatHardware::GetContext(EXACT_DENSE); + REQUIRE(context_hardware_1 == context_hardware_2); + + auto hardware_3 = ExaGeoStatHardware(DIAGONAL_APPROX, 7, 0); + auto context_hardware_3 = ExaGeoStatHardware::GetContext(DIAGONAL_APPROX); + REQUIRE(context_hardware_2 == context_hardware_3); + + auto hardware_4 = ExaGeoStatHardware(DIAGONAL_APPROX, 3, 0); + auto context_hardware_4 = ExaGeoStatHardware::GetContext(DIAGONAL_APPROX); + REQUIRE(context_hardware_3 == context_hardware_4); + + //In case of HICMA initialize multiple instances of the TLR hardware. +#ifdef USE_HICMA + // Verify they have same static HICMA context + auto hardware_5 = ExaGeoStatHardware(TILE_LOW_RANK, 4, 0); + REQUIRE(hardware_5.GetContext(TILE_LOW_RANK) != nullptr); + + auto hardware_6 = ExaGeoStatHardware(TILE_LOW_RANK, 4, 0); + REQUIRE(hardware_6.GetContext(TILE_LOW_RANK) != nullptr); + REQUIRE(hardware_5.GetContext(TILE_LOW_RANK) == hardware_6.GetContext(TILE_LOW_RANK)); + + // Verify they have same Chameleon context as the EXACT_DENSE and DIAGONAL_APPROX hardware + REQUIRE(hardware_5.GetContext(EXACT_DENSE) == hardware_6.GetContext(EXACT_DENSE)); + REQUIRE(hardware_5.GetContext(EXACT_DENSE) == hardware_1.GetContext(EXACT_DENSE)); +#endif + +} + +void TEST_CONTEXT_GETTER() { + + // Initialize multiple instances of hardware, and verify context getter results. + auto hardware_1 = ExaGeoStatHardware(EXACT_DENSE, 1, 0); + auto hardware_2 = ExaGeoStatHardware(DIAGONAL_APPROX, 1, 0); + + // Chameleon context is always non-null + REQUIRE(hardware_1.GetContext(EXACT_DENSE) != nullptr); + REQUIRE(hardware_2.GetContext(DIAGONAL_APPROX) != nullptr); + + //In case of HICMA, initialize TLR hardware. +#ifdef USE_HICMA + auto hardware_3 = ExaGeoStatHardware(TILE_LOW_RANK, 4, 0); + + // Hicma context and Chameleon context are always non-null + REQUIRE(hardware_3.GetContext(TILE_LOW_RANK) != nullptr); + REQUIRE(hardware_3.GetContext(EXACT_DENSE) != nullptr); + REQUIRE(hardware_3.GetContext(DIAGONAL_APPROX) != nullptr); +#else + // Otherwise an exception is raised + REQUIRE_THROWS(hardware_1.GetContext(TILE_LOW_RANK)); + REQUIRE_THROWS(hardware_2.GetContext(TILE_LOW_RANK)); +#endif +} + +TEST_CASE("ExaGeoStat Hardware Tests") { + TEST_HARDWARE_CONSTRUCTION(); + TEST_CONTEXT_GETTER(); + TEST_STATIC_CONTEXT(); +} + diff --git a/tests/cpp-tests/helpers/CMakeLists.txt b/tests/cpp-tests/helpers/CMakeLists.txt index 7e71df57..4f093909 100644 --- a/tests/cpp-tests/helpers/CMakeLists.txt +++ b/tests/cpp-tests/helpers/CMakeLists.txt @@ -5,13 +5,13 @@ # @file CMakeLists.txt # @version 1.1.0 # @author Mahmoud ElKarargy -# @author Sameh Abdulah -# @date 2023-12-08 +# @date 2024-01-24 set(EXAGEOSTAT_TESTFILES - ${CMAKE_CURRENT_SOURCE_DIR}/TestPredictionHelpers.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TestDiskWriter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TestDistanceCalculationHelpers.cpp ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE - ) +) diff --git a/tests/cpp-tests/helpers/TestDiskWriter.cpp b/tests/cpp-tests/helpers/TestDiskWriter.cpp new file mode 100644 index 00000000..0916ad65 --- /dev/null +++ b/tests/cpp-tests/helpers/TestDiskWriter.cpp @@ -0,0 +1,80 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestDiskWriter.cpp + * @brief Unit tests for the DiskWriter in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the class DiskWriter. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-24 +**/ + +#include +#include + +#include +#include + +using namespace exageostat::dataunits; +using namespace exageostat::common; + +void TEST_3D_VECTORS_WRITING() { + // Initialize data vectors + int N = 8; + auto *location_x = new double[N]{0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916}; + + auto *location_y = new double[N]{0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011}; + + auto *location_z = new double[N]{1, 1, 1, 1, + 1, 1, 1, 1}; + + auto *measurements_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065}; + + // Initialize Locations object, that will be written in file + Locations locations(N, Dimension3D); + locations.SetLocationX(*location_x, N); + locations.SetLocationY(*location_y, N); + locations.SetLocationZ(*location_z, N); + + const int p = 1; + + std::string write_path = PROJECT_SOURCE_DIR; + std::string expectedFilePath = write_path + "/synthetic_ds/SYN_" + std::to_string(N / p) + "_1"; + + // Write the data into file + exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N, p, write_path, locations); + REQUIRE(std::filesystem::exists(expectedFilePath)); + + std::ifstream file(expectedFilePath); + REQUIRE(file.is_open()); + + // Read the contents from the file + std::string line; + for (int i = 0; i < N / p; ++i) { + std::getline(file, line); + + std::ostringstream oss; + oss << std::setprecision(15) << locations.GetLocationX()[i] << ',' + << locations.GetLocationY()[i] << ',' << locations.GetLocationZ()[i] << ","<< std::setprecision(15) << measurements_matrix[i] ; + std::string expectedLine = oss.str(); + + REQUIRE(line == expectedLine); + } + + delete[] location_x; + delete[] location_y; + delete[] location_z; + delete[] measurements_matrix; +} +TEST_CASE("Disk Writer Tests"){ + TEST_3D_VECTORS_WRITING(); +} diff --git a/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp b/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp new file mode 100644 index 00000000..c4d9caab --- /dev/null +++ b/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp @@ -0,0 +1,144 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestDistanceCalculationHelpers.cpp + * @brief Unit tests for the DistanceCalculationHelpers.cpp in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the class DistanceCalculationHelpers. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-24 +**/ + +#include +#include + +#include +#include + +using namespace exageostat::common; +using namespace exageostat::helpers; +using namespace exageostat::dataunits; + +void TEST_RADIAN_CONVERSION() { + exageostat::helpers::DistanceCalculationHelpers distance_helper; + REQUIRE(distance_helper.DegreeToRadian(0) == Catch::Approx(0.0)); + REQUIRE(distance_helper.DegreeToRadian(90) == Catch::Approx(PI / 2)); + REQUIRE(distance_helper.DegreeToRadian(180) == Catch::Approx(PI)); +} + +void TEST_CALCULATE_DISTANCE() { + + SECTION("2D - Euclidean & Haversine Distance") { + int size = 2; + + // 2D - Locations initializations + auto *location_x = new double[size]{10.0, 4.0}; + auto *location_y = new double[size]{12.0, 4.0}; + + Locations location1(size, Dimension2D); + Locations location2(size, Dimension2D); + + location1.SetLocationX(*location_x, size); + location1.SetLocationY(*location_y, size); + location2.SetLocationX(*location_x, size); + location2.SetLocationY(*location_y, size); + + int idx1 = 0; // index of x-coordinate + int idx2 = 1; // index of y-coordinate + int flagZ = 0; // 2D case + + int distanceMetric = 0; // Default - Use Euclidean distance + REQUIRE(DistanceCalculationHelpers::CalculateDistance(location1, location2, idx1, idx2, distanceMetric, + flagZ) == 10); + + distanceMetric = 1; // Use Haversine distance + auto distance_earth = DistanceCalculationHelpers::DistanceEarth(location_x[idx1], location_y[idx1], + location_x[idx2], location_y[idx2]); + REQUIRE(DistanceCalculationHelpers::CalculateDistance(location1, location2, idx1, idx2, distanceMetric, + flagZ) == distance_earth); + + delete[] location_x; + delete[] location_y; + }SECTION("3D - Euclidean & Haversine Distance") { + int size = 3; + + // 3D - Locations initializations + auto *location_x = new double[size]{10.0, 4.0}; + auto *location_y = new double[size]{12.0, 4.0}; + auto *location_z = new double[size]{1, 2}; + + Locations location1(size, Dimension3D); + Locations location2(size, Dimension3D); + + location1.SetLocationX(*location_x, size); + location1.SetLocationY(*location_y, size); + location1.SetLocationZ(*location_z, size); + + location2.SetLocationX(*location_x, size); + location2.SetLocationY(*location_y, size); + location2.SetLocationZ(*location_z, size); + + int idx1 = 0; // index of x-coordinate + int idx2 = 1; // index of y-coordinate + int flagZ = 1; // 2D case + + int distanceMetric = 0; // Use Euclidean distance + REQUIRE(DistanceCalculationHelpers::CalculateDistance(location1, location2, idx1, idx2, distanceMetric, + flagZ) == Catch::Approx(10.05).epsilon(0.01)); + + distanceMetric = 1; // Use Haversine distance + REQUIRE_THROWS( + DistanceCalculationHelpers::CalculateDistance(location1, location2, idx1, idx2, distanceMetric, + flagZ)); + + delete[] location_x; + delete[] location_y; + delete[] location_z; + } +} + +void TEST_DISTANCE_EARTH() { + SECTION("Distance between two identical points") { + + double lat = 0.1, lon = 0.2; + REQUIRE(DistanceCalculationHelpers::DistanceEarth(lat, lon, lat, lon) == Catch::Approx(0.0)); + + }SECTION("Distance between North Pole and South Pole") { + + double north_pole_Lat = 90.0, pole_Lon = 0.0; + double south_pole_Lat = -90.0; + REQUIRE(DistanceCalculationHelpers::DistanceEarth(north_pole_Lat, pole_Lon, south_pole_Lat, pole_Lon) == + Catch::Approx(EARTH_RADIUS * M_PI)); + + }SECTION("Distance between two points on the equator 90 degrees apart") { + + double equator_Lat = 0.0; + double lon1 = 0.0, lon2 = 90.0; + REQUIRE(DistanceCalculationHelpers::DistanceEarth(equator_Lat, lon1, equator_Lat, lon2) == + Catch::Approx(EARTH_RADIUS * M_PI / 2)); + + }SECTION("Distance between two arbitrary points") { + + // Arbitrary location coordinates + double location1_lat = 40.7128; + double location1_lon = -74.0060; + double location2_lat = 34.0522; + double location2_lon = -118.2437; + + // The expected distance is approximately 3940 kilometers + double expectedDistance = 3940.0; + double calculatedDistance = DistanceCalculationHelpers::DistanceEarth(location1_lat, location1_lon, + location2_lat, location2_lon); + REQUIRE(calculatedDistance == Catch::Approx(expectedDistance).epsilon(0.01)); + + } +} + +TEST_CASE("Degree to Radian Conversion") { + TEST_RADIAN_CONVERSION(); + TEST_CALCULATE_DISTANCE(); + TEST_DISTANCE_EARTH(); +} \ No newline at end of file diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp index cf9ff7e5..74bf95a8 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp @@ -47,7 +47,7 @@ void TEST_KERNEL_GENERATION_BivariateMaternFlexible() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp index bbfcd5f5..2b8b5101 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp @@ -48,7 +48,7 @@ void TEST_KERNEL_GENERATION_BivariateMaternParsimonious() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations,data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp index 4258194f..c673306e 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp @@ -49,7 +49,7 @@ void TEST_KERNEL_GENERATION_BivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp index 531cb60b..e6d0057e 100644 --- a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp @@ -46,7 +46,7 @@ void TEST_KERNEL_GENERATION_TrivariateMaternParsimonious() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp index e7d97c30..27e9ce93 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp @@ -50,7 +50,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp index 380b4f93..0142e74c 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp @@ -46,7 +46,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNuggetsStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp index e7830f8f..7f00ccd5 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp @@ -47,7 +47,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp index 58144e6d..526022ba 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp @@ -48,7 +48,7 @@ void TEST_KERNEL_GENERATION_UnivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/prediction/CMakeLists.txt b/tests/cpp-tests/prediction/CMakeLists.txt new file mode 100644 index 00000000..9a1d4bec --- /dev/null +++ b/tests/cpp-tests/prediction/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-01-24 + +set(EXAGEOSTAT_TESTFILES + + ${CMAKE_CURRENT_SOURCE_DIR}/TestPrediction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TestPredictionHelpers.cpp + + ${EXAGEOSTAT_TESTFILES} + PARENT_SCOPE +) + diff --git a/tests/cpp-tests/prediction/TestPrediction.cpp b/tests/cpp-tests/prediction/TestPrediction.cpp new file mode 100644 index 00000000..74f1318f --- /dev/null +++ b/tests/cpp-tests/prediction/TestPrediction.cpp @@ -0,0 +1,189 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestPrediction.cpp + * @brief Unit tests for the TestPrediction class in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the TestPrediction class + * @version 1.0.0 + * @author Mahmoud ElKarargy + * @date 2024-1-18 +**/ + +#include + +#include + +using namespace std; + +using namespace exageostat::common; +using namespace exageostat::prediction; + +void TEST_PREDICTION_MISSING_DATA() { + + //Init configuration + Configurations configurations; + configurations.SetUnknownObservationsNb(4); + int N = 16; + configurations.SetProblemSize(N); + configurations.SetKernelName("UnivariateMaternStationary"); + int dts = 8; + + configurations.SetDenseTileSize(dts); + configurations.SetComputation(EXACT_DENSE); + configurations.SetMaxMleIterations(3); + configurations.SetTolerance(pow(10, -4)); + + vector lb{0.1, 0.1, 0.1}; + configurations.SetLowerBounds(lb); + configurations.SetStartingTheta(lb); + vector ub{5, 5, 5}; + configurations.SetUpperBounds(ub); + vector initial_theta{1, 0.1, 0.5}; + configurations.SetInitialTheta(initial_theta); + + + auto hardware = ExaGeoStatHardware(EXACT_DENSE, configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); + std::unique_ptr> data = std::make_unique>( + configurations.GetProblemSize(), configurations.GetDimension()); + + auto *z_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, + 0.290822066007430102}; + //creating locations x and y. + auto *location_x = new double[N]{0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, + 0.877592126344701295}; + auto *location_y = new double[N]{0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, + 0.942824444953078489}; + + data->GetLocations()->SetLocationX(*location_x, N); + data->GetLocations()->SetLocationY(*location_y, N); + + SECTION("Test Prediction - MSPE ") + { + configurations.SetIsIDW(false); + configurations.SetIsMLOEMMOM(false); + configurations.SetIsFisher(false); + configurations.SetIsMSPE(true); + + vector estimated_theta{0.9, 0.09, 0.4}; + configurations.SetEstimatedTheta(estimated_theta); + + // Register and create a kernel object + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); + + // Add the data prediction arguments. + configurations.InitializeDataPredictionArguments(); + Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + + REQUIRE(exageostat::results::Results::GetInstance()->GetMSPEError()== Catch::Approx(0.552448)); + delete pKernel; + } + SECTION("Test Prediction - IDW ") { + configurations.SetIsMLOEMMOM(false); + configurations.SetIsFisher(false); + configurations.SetIsMSPE(false); + configurations.SetIsIDW(true); + + vector estimated_theta{0.9, 0.09, 0.4}; + configurations.SetEstimatedTheta(estimated_theta); + std::vector idw_error={ 1.18856255, 1.25725881, 1.11986628 }; + + // Register and create a kernel object + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); + // Add the data prediction arguments. + configurations.InitializeDataPredictionArguments(); + Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + for(int i =0;i<3;i++){ + REQUIRE(exageostat::results::Results::GetInstance()->GetIDWError()[i] == Catch::Approx(idw_error[i])); + } + delete pKernel; + } + SECTION("Test Prediction - MLOE_MMOM ") { + configurations.SetIsMSPE(false); + configurations.SetIsIDW(false); + configurations.SetIsMLOEMMOM(true); + configurations.SetIsFisher(false); + + vector estimated_theta{0.9, 0.09, 0.4}; + configurations.SetEstimatedTheta(estimated_theta); + + // Register and create a kernel object + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); + // Add the data prediction arguments. + configurations.InitializeDataPredictionArguments(); + Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + REQUIRE(exageostat::results::Results::GetInstance()->GetMLOE() == Catch::Approx(0.004467).margin(0.001)); + REQUIRE(exageostat::results::Results::GetInstance()->GetMMOM() == Catch::Approx(-0.0812376).margin(0.001)); + + + vector new_estimated_theta1{1, 0.1, 0.5}; + configurations.SetEstimatedTheta(new_estimated_theta1); + // Add the data prediction arguments. + configurations.InitializeDataPredictionArguments(); + Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + REQUIRE(exageostat::results::Results::GetInstance()->GetMLOE() == Catch::Approx(0).margin(0.001)); + REQUIRE(exageostat::results::Results::GetInstance()->GetMMOM() == Catch::Approx(0).margin(0.001)); + delete pKernel; + } + + SECTION("Test Prediction - FISHER") { + configurations.SetIsMSPE(false); + configurations.SetIsIDW(false); + configurations.SetIsMLOEMMOM(false); + configurations.SetIsFisher(true); + + vector new_estimated_theta{0.9, 0.09, 0.4}; + configurations.SetEstimatedTheta(new_estimated_theta); + // Register and create a kernel object + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); + // Add the data prediction arguments. + configurations.InitializeDataPredictionArguments(); + Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + + REQUIRE(exageostat::results::Results::GetInstance()->GetFisher00() == Catch::Approx(0.104589)); + REQUIRE(exageostat::results::Results::GetInstance()->GetFisher11() == Catch::Approx(0.187355)); + REQUIRE(exageostat::results::Results::GetInstance()->GetFisher22() == Catch::Approx(10.556483)); + delete pKernel; + } + SECTION("Test Prediction - Exception"){ + vector new_estimated_theta{-1, -1, -1}; + configurations.SetEstimatedTheta(new_estimated_theta); + Prediction predictor; + // Register and create a kernel object + configurations.SetIsMLOEMMOM(true); + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); + REQUIRE_THROWS(predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel)); + delete pKernel; + } + delete[] location_x; + delete[] location_y; + delete[] z_matrix; +} + +TEST_CASE("Test Predictions") { + TEST_PREDICTION_MISSING_DATA(); +} \ No newline at end of file diff --git a/tests/cpp-tests/helpers/TestPredictionHelpers.cpp b/tests/cpp-tests/prediction/TestPredictionHelpers.cpp similarity index 99% rename from tests/cpp-tests/helpers/TestPredictionHelpers.cpp rename to tests/cpp-tests/prediction/TestPredictionHelpers.cpp index cefd8e79..8eeb92dc 100644 --- a/tests/cpp-tests/helpers/TestPredictionHelpers.cpp +++ b/tests/cpp-tests/prediction/TestPredictionHelpers.cpp @@ -4,7 +4,7 @@ // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). /** - * @file TestPrediction.cpp + * @file TestPredictionHelpers.cpp * @brief Unit tests for the TestPrediction class in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestPrediction class * @version 1.1.0 diff --git a/tests/cpp-tests/results/CMakeLists.txt b/tests/cpp-tests/results/CMakeLists.txt new file mode 100644 index 00000000..4ef3c229 --- /dev/null +++ b/tests/cpp-tests/results/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-01-24 + +set(EXAGEOSTAT_TESTFILES + ${CMAKE_CURRENT_SOURCE_DIR}/TestResults.cpp + ${EXAGEOSTAT_TESTFILES} + PARENT_SCOPE +) diff --git a/tests/cpp-tests/results/TestResults.cpp b/tests/cpp-tests/results/TestResults.cpp new file mode 100644 index 00000000..a847c126 --- /dev/null +++ b/tests/cpp-tests/results/TestResults.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestResults.cpp + * @brief Unit tests for the Results class in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the Results class + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-24 +**/ + +#include + +#include + +using namespace exageostat::results; + +void TEST_SINGLETON_RESULTS() { + //Test singleton instance of results + auto results1 = Results::GetInstance(); + auto results2 = Results::GetInstance(); + REQUIRE(results1 == results2); +} + +void TEST_SETTERS_AND_GETTERS() { + + auto results_instacne = Results::GetInstance(); + + SECTION("Total Modeling Execution Time Setter/Getter") { + results_instacne->SetTotalModelingExecutionTime(1.0); + REQUIRE(results_instacne->GetTotalModelingExecutionTime() == 1.0); + + }SECTION("Total Modeling Flops Setter/Getter") { + results_instacne->SetTotalModelingFlops(1.0); + REQUIRE(results_instacne->GetTotalModelingFlops() == 1.0); + + }SECTION("Avg Modeling Execution Time Setter/Getter") { + REQUIRE_THROWS(results_instacne->GetAverageModelingExecutionTime()); + + results_instacne->SetMLEIterations(2.0); + results_instacne->SetTotalModelingExecutionTime(4.0); + REQUIRE(results_instacne->GetAverageModelingExecutionTime() == 2.0); + + }SECTION("Avg Modeling Flops Setter/Getter") { + results_instacne->SetMLEIterations(0); + REQUIRE_THROWS(results_instacne->GetAverageModelingFlops()); + + results_instacne->SetMLEIterations(2.0); + results_instacne->SetTotalModelingFlops(4.0); + REQUIRE(results_instacne->GetAverageModelingFlops() == 2.0); + } +} + +TEST_CASE("Test Results") { + TEST_SINGLETON_RESULTS(); + TEST_SETTERS_AND_GETTERS(); +} diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index 1d71687c..49c2d61e 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -14,6 +14,9 @@ #include #include #include +#ifdef USE_CUDA +#include +#endif #include @@ -131,9 +134,11 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp } arguments_vector.push_back("--cores=" + to_string(cpu_size_distribution(gen))); - // TODO: Till fixing cuda error with multiple devices. #ifdef USE_CUDA - arguments_vector.push_back("--gpus=1"); + int nDevices; + cudaGetDeviceCount(&nDevices); + uniform_int_distribution gpu_size_distribution(1, nDevices); + arguments_vector.push_back("--gpus=" + to_string(gpu_size_distribution(gen))); #endif arguments_vector.push_back("--kernel=" + aKernelName); From 21e826bcb2e0795d6dde0833f2ce1da53b8751ea Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 11 Feb 2024 23:10:03 +0200 Subject: [PATCH 23/82] all release issue without src checks --- .gitignore | 2 +- CHANGELOG.md | 1 + inst/include/common/Definitions.hpp | 6 +- .../include/data-generators/DataGenerator.hpp | 2 +- inst/include/hardware/ExaGeoStatHardware.hpp | 28 +-- inst/include/results/Results.hpp | 42 ++++ src/data-generators/DataGenerator.cpp | 15 +- src/hardware/ExaGeoStatHardware.cpp | 58 +++-- src/prediction/Prediction.cpp | 3 +- src/results/Results.cpp | 35 +++- tests/cpp-tests/CMakeLists.txt | 15 +- .../configurations/TestConfigurations.cpp | 71 ++++++- tests/cpp-tests/data-units/CMakeLists.txt | 14 ++ .../data-units/TestDescriptorData.cpp | 66 ++++++ tests/cpp-tests/hardware/CMakeLists.txt | 14 ++ .../hardware/TestExaGeoStatHardware.cpp | 113 ++++++++++ tests/cpp-tests/helpers/CMakeLists.txt | 10 +- tests/cpp-tests/helpers/TestDiskWriter.cpp | 80 +++++++ .../TestDistanceCalculationHelpers.cpp | 144 +++++++++++++ tests/cpp-tests/prediction/CMakeLists.txt | 18 ++ tests/cpp-tests/prediction/TestPrediction.cpp | 198 ++++++++++++++++++ .../TestPredictionHelpers.cpp | 2 +- tests/cpp-tests/results/CMakeLists.txt | 14 ++ tests/cpp-tests/results/TestResults.cpp | 59 ++++++ 24 files changed, 933 insertions(+), 77 deletions(-) create mode 100644 tests/cpp-tests/data-units/CMakeLists.txt create mode 100644 tests/cpp-tests/data-units/TestDescriptorData.cpp create mode 100644 tests/cpp-tests/hardware/CMakeLists.txt create mode 100644 tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp create mode 100644 tests/cpp-tests/helpers/TestDiskWriter.cpp create mode 100644 tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp create mode 100644 tests/cpp-tests/prediction/CMakeLists.txt create mode 100644 tests/cpp-tests/prediction/TestPrediction.cpp rename tests/cpp-tests/{helpers => prediction}/TestPredictionHelpers.cpp (99%) create mode 100644 tests/cpp-tests/results/CMakeLists.txt create mode 100644 tests/cpp-tests/results/TestResults.cpp diff --git a/.gitignore b/.gitignore index 4ab21a78..d23b41d2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # synthetic data for testing tests/cpp-tests/data-generators/concrete/synthetic_ds +synthetic_ds/ # intallation directory installdir/ @@ -672,7 +673,6 @@ bh_unicode_properties.cache GitHub.sublime-settings # Sesimic Toolbox Results Specifics -results/ *.trace *.segy *.sgy diff --git a/CHANGELOG.md b/CHANGELOG.md index 488c1380..41fc89f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Introduced a benchmarking script. - .gitignore file. - More examples. +- Add tests for all src files. ### Fixed - Resolved issues with MPI installation. diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index 8bd8e341..e7507436 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -128,10 +128,10 @@ namespace exageostat::common { }; /** - * @enum Descriptor Type - * @brief Enum denoting the Descriptor Type. + * @enum Data source Type + * @brief Enum denoting the data source Type. */ - enum DataGeneratorType { + enum DataSourceType { SYNTHETIC = 0, CSV_FILE = 1 }; diff --git a/inst/include/data-generators/DataGenerator.hpp b/inst/include/data-generators/DataGenerator.hpp index 06aed2c2..14cdf110 100644 --- a/inst/include/data-generators/DataGenerator.hpp +++ b/inst/include/data-generators/DataGenerator.hpp @@ -63,7 +63,7 @@ namespace exageostat::generators { protected: /// Used enum for data generators types. - static common::DataGeneratorType aDataGeneratorType; + static common::DataSourceType aDataSourceType; }; /** diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index b7e5c236..c272c65d 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -9,7 +9,7 @@ * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-08-07 + * @date 2024-01-24 **/ #ifndef EXAGEOSTATCPP_EXAGEOSTATHARDWARE_HPP @@ -44,18 +44,14 @@ namespace exageostat::hardware { * @return Pointer to the hardware context. * */ - [[nodiscard]] void *GetChameleonContext() const; + [[nodiscard]] static void *GetChameleonContext() ; -#ifdef USE_HICMA - -/** - * @brief Get the Hicma hardware context. - * @return Pointer to the hardware context. - * - */ - [[nodiscard]] void *GetHicmaContext() const; - -#endif + /** + * @brief Get the Hicma hardware context. + * @return Pointer to the hardware context. + * + */ + [[nodiscard]] static void *GetHicmaContext(); /** * @brief Get the hardware context. @@ -63,15 +59,13 @@ namespace exageostat::hardware { * @return Pointer to the hardware context. * */ - [[nodiscard]] void *GetContext(common::Computation aComputation) const; + [[nodiscard]] static void *GetContext(common::Computation aComputation) ; private: //// Used Pointer to the Chameleon hardware context. - void *mpChameleonContext = nullptr; -#ifdef USE_HICMA + static void *mpChameleonContext; //// Used Pointer to the Hicma hardware context. - void *mpHicmaContext = nullptr; -#endif + static void *mpHicmaContext; //// Used Computation mode for the solver. common::Computation mComputation; }; diff --git a/inst/include/results/Results.hpp b/inst/include/results/Results.hpp index b112284c..8037dd48 100644 --- a/inst/include/results/Results.hpp +++ b/inst/include/results/Results.hpp @@ -95,6 +95,48 @@ namespace exageostat::results { */ [[nodiscard]] double GetTotalModelingExecutionTime() const; + /** + * @brief Get the MLOE. + * @return The MLOE. + */ + [[nodiscard]] double GetMLOE() const; + + /** + * @brief Get the MSPEError. + * @return The MSPEError. + */ + [[nodiscard]] double GetMSPEError() const; + + /** + * @brief Get the IDW error. + * @return The the IDW error vector. + */ + [[nodiscard]] std::vector GetIDWError() const; + + /** + * @brief Get the MMOM. + * @return The MMOM. + */ + [[nodiscard]] double GetMMOM() const; + + /** + * @brief Get the Fisher matrix element 00. + * @return the Fisher matrix element 00. + */ + [[nodiscard]] double GetFisher00() const; + + /** + * @brief Get the Fisher matrix element 11. + * @return the Fisher matrix element 11. + */ + [[nodiscard]] double GetFisher11() const; + + /** + * @brief Get the Fisher matrix element 22. + * @return the Fisher matrix element 22. + */ + [[nodiscard]] double GetFisher22() const; + /** * @brief Set the total modeling FLOPs. * @param[in] aTime The total number of FLOPs for data modeling. diff --git a/src/data-generators/DataGenerator.cpp b/src/data-generators/DataGenerator.cpp index a0515b68..8344241a 100644 --- a/src/data-generators/DataGenerator.cpp +++ b/src/data-generators/DataGenerator.cpp @@ -34,17 +34,18 @@ std::unique_ptr> DataGenerator::CreateGenerator(Configuratio throw std::domain_error("Please activate either the synthetic or the CSV file for data generation"); } if(apConfigurations.GetIsSynthetic()){ - aDataGeneratorType = SYNTHETIC; + aDataSourceType = SYNTHETIC; } else if(apConfigurations.GetIsCSV()){ - aDataGeneratorType = CSV_FILE; + aDataSourceType = CSV_FILE; } results::Results::GetInstance()->SetIsSynthetic(apConfigurations.GetIsSynthetic()); // Return DataGenerator unique pointer of Synthetic type - if (aDataGeneratorType == SYNTHETIC) { + //// TODO: In case of other file support, Then we can create another layer for the factory creation depending on the file size. + if (aDataSourceType == SYNTHETIC) { return std::unique_ptr>(SyntheticGenerator::GetInstance()); - } else if (aDataGeneratorType == CSV_FILE) { + } else if (aDataSourceType == CSV_FILE) { return std::unique_ptr>(CSVLoader::GetInstance()); } else { throw std::runtime_error("Data Loading for this file type is unsupported for now"); @@ -54,9 +55,9 @@ std::unique_ptr> DataGenerator::CreateGenerator(Configuratio template DataGenerator::~DataGenerator() { // Return DataGenerator unique pointer of Synthetic type - if (aDataGeneratorType == SYNTHETIC) { + if (aDataSourceType == SYNTHETIC) { SyntheticGenerator::GetInstance()->ReleaseInstance(); - } else if (aDataGeneratorType == CSV_FILE) { + } else if (aDataSourceType == CSV_FILE) { CSVLoader::GetInstance()->ReleaseInstance(); } else { std::cerr << "Data Loading for this file type is unsupported for now" << std::endl; @@ -64,4 +65,4 @@ DataGenerator::~DataGenerator() { } } -template DataGeneratorType DataGenerator::aDataGeneratorType = common::SYNTHETIC; +template DataSourceType DataGenerator::aDataSourceType = common::SYNTHETIC; diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index f33a67b4..7f2c38e6 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -9,7 +9,7 @@ * @version 1.0.1 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-08-07 + * @date 2024-01-24 **/ #include @@ -23,51 +23,44 @@ using namespace exageostat::hardware; ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber) { - LOGGER("** Initialise ExaGeoStat hardware **") this->mComputation = aComputation; int tag_width = 31, tag_sep = 26; + // Init hardware using Chameleon - if (!this->mpChameleonContext) { + if (!mpChameleonContext) { CHAMELEON_user_tag_size(tag_width, tag_sep); CHAMELEON_Init(aCoreNumber, aGpuNumber) - this->mpChameleonContext = chameleon_context_self(); + mpChameleonContext = chameleon_context_self(); } // Init hardware using Hicma if (aComputation == common::TILE_LOW_RANK) { #ifdef USE_HICMA - if (!this->mpHicmaContext) { + if (!mpHicmaContext) { HICMA_user_tag_size(tag_width, tag_sep); HICMA_Init(aCoreNumber, aGpuNumber); - this->mpHicmaContext = hicma_context_self(); + mpHicmaContext = hicma_context_self(); } #else - throw std::runtime_error("You need to enable Hicma to use TLR computation!"); + throw std::runtime_error("You need to enable HiCMA to use TLR computation!"); #endif } helpers::CommunicatorMPI::GetInstance()->SetHardwareInitialization(); } ExaGeoStatHardware::~ExaGeoStatHardware() { - // finalize hardware using Hicma // finalize hardware using Chameleon - if (!this->mpChameleonContext) { - std::cerr << "No initialized context of Chameleon, Please initialize a hardware first" << std::endl; - exit(1); - } else { + if (mpChameleonContext) { CHAMELEON_Finalize() - this->mpChameleonContext = nullptr; + mpChameleonContext = nullptr; } + // finalize hardware using HiCMA if (this->mComputation == common::TILE_LOW_RANK) { #ifdef USE_HICMA - if (!this->mpHicmaContext) { - std::cout - << "No initialized context of HiCMA, Please use 'ExaGeoStatHardware::ExaGeoStatHardware(aComputation, CoreNumber, aGpuNumber);'" - << std::endl; - } else { + if (mpHicmaContext) { HICMA_Finalize(); - this->mpHicmaContext = nullptr; + mpHicmaContext = nullptr; } #endif } @@ -75,32 +68,29 @@ ExaGeoStatHardware::~ExaGeoStatHardware() { results::Results::GetInstance()->PrintEndSummary(); } -#ifdef USE_HICMA - -void *ExaGeoStatHardware::GetHicmaContext() const { - if (!this->mpHicmaContext) { - throw std::runtime_error("Hardware is not initialized!"); +void *ExaGeoStatHardware::GetHicmaContext() { + if (!mpHicmaContext) { + throw std::runtime_error("HiCMA Hardware is not initialized!"); } - return this->mpHicmaContext; + return mpHicmaContext; } -#endif - -void *ExaGeoStatHardware::GetChameleonContext() const { - if (!this->mpChameleonContext) { - throw std::runtime_error("Hardware is not initialized!"); +void *ExaGeoStatHardware::GetChameleonContext() { + if (!mpChameleonContext) { + throw std::runtime_error("Chameleon Hardware is not initialized!"); } - return this->mpChameleonContext; + return mpChameleonContext; } -void *ExaGeoStatHardware::GetContext(common::Computation aComputation) const { +void *ExaGeoStatHardware::GetContext(common::Computation aComputation) { if (aComputation == common::EXACT_DENSE || aComputation == common::DIAGONAL_APPROX) { return GetChameleonContext(); } if (aComputation == common::TILE_LOW_RANK) { -#ifdef USE_HICMA return GetHicmaContext(); -#endif } return nullptr; } + +void * ExaGeoStatHardware::mpChameleonContext = nullptr; +void * ExaGeoStatHardware::mpHicmaContext = nullptr; \ No newline at end of file diff --git a/src/prediction/Prediction.cpp b/src/prediction/Prediction.cpp index dbb9817d..08804b55 100644 --- a/src/prediction/Prediction.cpp +++ b/src/prediction/Prediction.cpp @@ -36,6 +36,7 @@ void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHard break; } } + if (!can_predict && (aConfigurations.GetIsMLOEMMOM() || aConfigurations.GetIsMSPE() || aConfigurations.GetIsFisher())) { throw std::runtime_error( @@ -48,7 +49,7 @@ void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHard int n_z_obs = aConfigurations.CalculateZObsNumber(); auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(common::EXACT_DENSE); - //FISHER Prediction Function Call + // FISHER Prediction Function Call if (aConfigurations.GetIsFisher()) { LOGGER("---- Using Prediction Function Fisher ----") T *fisher_results; diff --git a/src/results/Results.cpp b/src/results/Results.cpp index 46805935..63234f6c 100644 --- a/src/results/Results.cpp +++ b/src/results/Results.cpp @@ -116,7 +116,7 @@ void Results::PrintEndSummary() { LOGGER("") } } - if(this->mFisher00 != 0){ + if (this->mFisher00 != 0) { LOGGER(" #Fisher") LOGGER(" #Sd For Sigma2: " << this->mFisher00) LOGGER(" #Sd For Alpha: " << this->mFisher11) @@ -242,4 +242,35 @@ void Results::SetFisher11(double aFisher11) { void Results::SetFisher22(double aFisher22) { this->mFisher22 = aFisher22; -} \ No newline at end of file +} + + +double Results::GetMLOE() const { + return this->mMLOE; +} + +double Results::GetMSPEError() const { + return this->mMSPEError; +} + +std::vector Results::GetIDWError() const { + return this->mIDWError; +} + +double Results::GetMMOM() const { + return this->mMMOM; +} + +double Results::GetFisher00() const { + return this->mFisher00; +} + + +double Results::GetFisher11() const { + return this->mFisher11; +} + +double Results::GetFisher22() const { + return this->mFisher22; +} + diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index cff029b3..e220bf7f 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -6,14 +6,21 @@ # @file CMakeLists.txt # @version 1.0.1 # @author Mahmoud ElKarargy -# @date 2023-01-31 +# @date 2024-01-24 add_subdirectory(api) -add_subdirectory(linear-algebra-solvers) -add_subdirectory(data-generators) add_subdirectory(configurations) -add_subdirectory(kernels) +add_subdirectory(data-generators) +add_subdirectory(hardware) add_subdirectory(helpers) +add_subdirectory(kernels) +add_subdirectory(linear-algebra-solvers) +add_subdirectory(prediction) +add_subdirectory(results) + +if(USE_HICMA) + add_subdirectory(data-units) +endif () enable_testing() add_executable(exageostat-tests ${EXAGEOSTAT_TESTFILES}) diff --git a/tests/cpp-tests/configurations/TestConfigurations.cpp b/tests/cpp-tests/configurations/TestConfigurations.cpp index 7e057335..59da0f29 100644 --- a/tests/cpp-tests/configurations/TestConfigurations.cpp +++ b/tests/cpp-tests/configurations/TestConfigurations.cpp @@ -25,6 +25,74 @@ using namespace std; using namespace exageostat::common; using namespace exageostat::configurations; +void TEST_ARGUMENT_INITIALIZATION(){ + + const int argc = 17; + char* argv[] = { + const_cast("program_name"), + const_cast("--N=16"), + const_cast("--dts=8"), + const_cast("--kernel=univariate_matern_stationary"), + const_cast("--computation=exact"), + const_cast("--precision=double"), + const_cast("--initial_theta=1:0.1:0.5"), + const_cast("--ub=5:5:5"), + const_cast("--lb=0.1:0.1:0.1"), + const_cast("--max_mle_iterations=5"), + const_cast("--tolerance=4"), + const_cast("--ZMiss=6"), + const_cast("--mspe"), + const_cast("--idw"), + const_cast("--mloe-mmom"), + const_cast("--fisher"), + const_cast("--data_path=./dummy-path") + }; + + Configurations configurations; + + // Initialize configuration dictionary with only common arguments + configurations.InitializeArguments(argc, argv); + + REQUIRE(configurations.GetProblemSize() == 16); + REQUIRE(configurations.GetKernelName() == "UnivariateMaternStationary"); + REQUIRE(configurations.GetDenseTileSize() == 8); + REQUIRE(configurations.GetPrecision() == DOUBLE); + + // No data generation arguments initialized + REQUIRE(configurations.GetDataPath() == string("")); + + // No data modeling arguments initialized + REQUIRE_THROWS(configurations.GetMaxMleIterations()); + REQUIRE_THROWS(configurations.GetTolerance()); + + // No data prediction arguments initialized + REQUIRE(configurations.GetIsMSPE() == false); + REQUIRE(configurations.GetIsIDW() == false); + REQUIRE(configurations.GetIsFisher() == false); + REQUIRE(configurations.GetIsMLOEMMOM() == false); + REQUIRE(configurations.GetUnknownObservationsNb() == 0); + + // Data generation arguments initialized + configurations.InitializeDataGenerationArguments(); + + REQUIRE(configurations.GetDataPath() == string("./dummy-path")); + + // Data modelling arguments initialized + configurations.InitializeDataModelingArguments(); + + REQUIRE(configurations.GetMaxMleIterations() == 5); + REQUIRE(configurations.GetTolerance() == 4); + + // Data prediction arguments initialized + configurations.InitializeDataPredictionArguments(); + + REQUIRE(configurations.GetIsMSPE() == true); + REQUIRE(configurations.GetIsIDW() == true); + REQUIRE(configurations.GetIsFisher() == true); + REQUIRE(configurations.GetIsMLOEMMOM() == true); + REQUIRE(configurations.GetUnknownObservationsNb() == 6); + +} void TEST_SYNTHETIC_CONFIGURATIONS() { Configurations synthetic_data_configurations; @@ -113,7 +181,8 @@ void TEST_COPY_CONSTRUCTOR() { } } -TEST_CASE("Synthetic Data Configurations") { +TEST_CASE("Configurations Tests") { TEST_SYNTHETIC_CONFIGURATIONS(); TEST_COPY_CONSTRUCTOR(); + TEST_ARGUMENT_INITIALIZATION(); } diff --git a/tests/cpp-tests/data-units/CMakeLists.txt b/tests/cpp-tests/data-units/CMakeLists.txt new file mode 100644 index 00000000..2abfab94 --- /dev/null +++ b/tests/cpp-tests/data-units/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2024-01-24 + +set(EXAGEOSTAT_TESTFILES + ${CMAKE_CURRENT_SOURCE_DIR}/TestDescriptorData.cpp + ${EXAGEOSTAT_TESTFILES} + PARENT_SCOPE + ) diff --git a/tests/cpp-tests/data-units/TestDescriptorData.cpp b/tests/cpp-tests/data-units/TestDescriptorData.cpp new file mode 100644 index 00000000..cdeb6f5f --- /dev/null +++ b/tests/cpp-tests/data-units/TestDescriptorData.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestDescriptorData.cpp + * @brief Tests for CHAMELEON to HICMA descriptor conversion in ExaGeoStat. + * @details This test case verifies the conversion of matrix descriptors from the CHAMELEON format to the HICMA format. + * It ensures that key properties of the matrix descriptor, such as dimensions, block sizes, and grid distribution parameters, are preserved during the conversion process. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-02-04 +**/ + +#include + +#include + +using namespace std; + +using namespace exageostat::linearAlgebra; +using namespace exageostat::common; +using namespace exageostat::configurations; +using namespace exageostat::dataunits; +using namespace exageostat::hardware; + +void TEST_CHAM_TO_HICMA_CONV(){ + + // Initialize Configuration + Configurations synthetic_data_configurations; + synthetic_data_configurations.SetProblemSize(4); + synthetic_data_configurations.SetDenseTileSize(1); + + // Initialize linear algebra solver + int p = 1; + auto hardware = ExaGeoStatHardware(EXACT_DENSE, 1, 0); + auto linearAlgebraSolver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); + linearAlgebraSolver->SetContext(hardware.GetChameleonContext()); + auto *data = new DescriptorData(); + linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, p); + + // Create CHAM descriptor and convert it to HICMA descriptor + auto *CHAM_descriptorC = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; + auto *HICMA_descriptor = data->ConvertChameleonToHicma(CHAM_descriptorC); + + // Verify common attributes are of same value + REQUIRE(CHAM_descriptorC->m == HICMA_descriptor->m); + REQUIRE(CHAM_descriptorC->n == HICMA_descriptor->n); + REQUIRE(CHAM_descriptorC->mb == HICMA_descriptor->mb); + REQUIRE(CHAM_descriptorC->nb == HICMA_descriptor->nb); + REQUIRE(CHAM_descriptorC->bsiz == HICMA_descriptor->bsiz); + REQUIRE(CHAM_descriptorC->i == HICMA_descriptor->i); + REQUIRE(CHAM_descriptorC->j == HICMA_descriptor->j); + REQUIRE(CHAM_descriptorC->mt == HICMA_descriptor->mt); + REQUIRE(CHAM_descriptorC->nt == HICMA_descriptor->nt); + REQUIRE(CHAM_descriptorC->lm == HICMA_descriptor->lm); + REQUIRE(CHAM_descriptorC->ln == HICMA_descriptor->ln); + REQUIRE(CHAM_descriptorC->p == HICMA_descriptor->p); + REQUIRE(CHAM_descriptorC->q == HICMA_descriptor->q); + + delete data; +} + +TEST_CASE(" CHAMELEON To HICMA Converter"){ + TEST_CHAM_TO_HICMA_CONV(); +} diff --git a/tests/cpp-tests/hardware/CMakeLists.txt b/tests/cpp-tests/hardware/CMakeLists.txt new file mode 100644 index 00000000..059454b0 --- /dev/null +++ b/tests/cpp-tests/hardware/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2024-01-24 + +set(EXAGEOSTAT_TESTFILES + ${CMAKE_CURRENT_SOURCE_DIR}/TestExaGeoStatHardware.cpp + ${EXAGEOSTAT_TESTFILES} + PARENT_SCOPE +) diff --git a/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp b/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp new file mode 100644 index 00000000..89f1459f --- /dev/null +++ b/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp @@ -0,0 +1,113 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestExaGeoStatHardware.cpp + * @brief Unit tests for the ExaGeoStatHardware class in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the ExaGeoStatHardware class + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-01-24 +**/ + +#include + +#include + +using namespace exageostat::hardware; +using namespace exageostat::common; +using namespace exageostat::hardware; + +void TEST_HARDWARE_CONSTRUCTION() { + + //Initialize multiple instances of the hardware, and verify chameleon context is not null. + auto hardware_1 = ExaGeoStatHardware(EXACT_DENSE, 4, 0); + REQUIRE(hardware_1.GetChameleonContext() != nullptr); + + auto hardware_2 = ExaGeoStatHardware(DIAGONAL_APPROX, 4, 0); + REQUIRE(hardware_2.GetChameleonContext() != nullptr); + REQUIRE(hardware_1.GetChameleonContext() == hardware_2.GetChameleonContext()); + + //In case of HICMA initialize a TLR hardware and verify non-null context, + // otherwise exception is raised in case of initialization. +#ifdef USE_HICMA + auto hardware_3 = ExaGeoStatHardware(TILE_LOW_RANK, 4, 0); + REQUIRE(hardware_3.GetHicmaContext() != nullptr); + + auto hardware_4 = ExaGeoStatHardware(TILE_LOW_RANK, 4, 0); + REQUIRE(hardware_4.GetHicmaContext() != nullptr); + REQUIRE(hardware_3.GetHicmaContext() == hardware_4.GetHicmaContext()); +#else + REQUIRE_THROWS(ExaGeoStatHardware(TILE_LOW_RANK, 4, 0)); +#endif + +} + +void TEST_STATIC_CONTEXT() { + + //Initialize multiple instances of the hardware: EXACT_DENSE,DIAGONAL_APPROX ,and verify they all have same static Chameleon context. + auto hardware_1 = ExaGeoStatHardware(EXACT_DENSE, 4, 0); + auto context_hardware_1 = ExaGeoStatHardware::GetContext(EXACT_DENSE); + + auto hardware_2 = ExaGeoStatHardware(EXACT_DENSE, 1, 0); + auto context_hardware_2 = ExaGeoStatHardware::GetContext(EXACT_DENSE); + REQUIRE(context_hardware_1 == context_hardware_2); + + auto hardware_3 = ExaGeoStatHardware(DIAGONAL_APPROX, 7, 0); + auto context_hardware_3 = ExaGeoStatHardware::GetContext(DIAGONAL_APPROX); + REQUIRE(context_hardware_2 == context_hardware_3); + + auto hardware_4 = ExaGeoStatHardware(DIAGONAL_APPROX, 3, 0); + auto context_hardware_4 = ExaGeoStatHardware::GetContext(DIAGONAL_APPROX); + REQUIRE(context_hardware_3 == context_hardware_4); + + //In case of HICMA initialize multiple instances of the TLR hardware. +#ifdef USE_HICMA + // Verify they have same static HICMA context + auto hardware_5 = ExaGeoStatHardware(TILE_LOW_RANK, 4, 0); + REQUIRE(hardware_5.GetContext(TILE_LOW_RANK) != nullptr); + + auto hardware_6 = ExaGeoStatHardware(TILE_LOW_RANK, 4, 0); + REQUIRE(hardware_6.GetContext(TILE_LOW_RANK) != nullptr); + REQUIRE(hardware_5.GetContext(TILE_LOW_RANK) == hardware_6.GetContext(TILE_LOW_RANK)); + + // Verify they have same Chameleon context as the EXACT_DENSE and DIAGONAL_APPROX hardware + REQUIRE(hardware_5.GetContext(EXACT_DENSE) == hardware_6.GetContext(EXACT_DENSE)); + REQUIRE(hardware_5.GetContext(EXACT_DENSE) == hardware_1.GetContext(EXACT_DENSE)); +#endif + +} + +void TEST_CONTEXT_GETTER() { + + // Initialize multiple instances of hardware, and verify context getter results. + auto hardware_1 = ExaGeoStatHardware(EXACT_DENSE, 1, 0); + auto hardware_2 = ExaGeoStatHardware(DIAGONAL_APPROX, 1, 0); + + // Chameleon context is always non-null + REQUIRE(hardware_1.GetContext(EXACT_DENSE) != nullptr); + REQUIRE(hardware_2.GetContext(DIAGONAL_APPROX) != nullptr); + + //In case of HICMA, initialize TLR hardware. +#ifdef USE_HICMA + auto hardware_3 = ExaGeoStatHardware(TILE_LOW_RANK, 4, 0); + + // Hicma context and Chameleon context are always non-null + REQUIRE(hardware_3.GetContext(TILE_LOW_RANK) != nullptr); + REQUIRE(hardware_3.GetContext(EXACT_DENSE) != nullptr); + REQUIRE(hardware_3.GetContext(DIAGONAL_APPROX) != nullptr); +#else + // Otherwise an exception is raised + REQUIRE_THROWS(hardware_1.GetContext(TILE_LOW_RANK)); + REQUIRE_THROWS(hardware_2.GetContext(TILE_LOW_RANK)); +#endif +} + +TEST_CASE("ExaGeoStat Hardware Tests") { + TEST_HARDWARE_CONSTRUCTION(); + TEST_CONTEXT_GETTER(); + TEST_STATIC_CONTEXT(); +} + diff --git a/tests/cpp-tests/helpers/CMakeLists.txt b/tests/cpp-tests/helpers/CMakeLists.txt index 4bae3c7a..b2943b6c 100644 --- a/tests/cpp-tests/helpers/CMakeLists.txt +++ b/tests/cpp-tests/helpers/CMakeLists.txt @@ -3,15 +3,15 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy -# @author Sameh Abdulah -# @date 2023-12-08 +# @date 2024-01-24 set(EXAGEOSTAT_TESTFILES - ${CMAKE_CURRENT_SOURCE_DIR}/TestPredictionHelpers.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TestDiskWriter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TestDistanceCalculationHelpers.cpp ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE - ) +) diff --git a/tests/cpp-tests/helpers/TestDiskWriter.cpp b/tests/cpp-tests/helpers/TestDiskWriter.cpp new file mode 100644 index 00000000..ee3d1450 --- /dev/null +++ b/tests/cpp-tests/helpers/TestDiskWriter.cpp @@ -0,0 +1,80 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestDiskWriter.cpp + * @brief Unit tests for the DiskWriter in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the class DiskWriter. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-01-24 +**/ + +#include +#include + +#include +#include + +using namespace exageostat::dataunits; +using namespace exageostat::common; + +void TEST_3D_VECTORS_WRITING() { + // Initialize data vectors + int N = 8; + auto *location_x = new double[N]{0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916}; + + auto *location_y = new double[N]{0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011}; + + auto *location_z = new double[N]{1, 1, 1, 1, + 1, 1, 1, 1}; + + auto *measurements_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065}; + + // Initialize Locations object, that will be written in file + Locations locations(N, Dimension3D); + locations.SetLocationX(*location_x, N); + locations.SetLocationY(*location_y, N); + locations.SetLocationZ(*location_z, N); + + const int p = 1; + + std::string write_path = PROJECT_SOURCE_DIR; + std::string expectedFilePath = write_path + "/synthetic_ds/SYN_" + std::to_string(N / p) + "_1"; + + // Write the data into file + exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N, p, write_path, locations); + REQUIRE(std::filesystem::exists(expectedFilePath)); + + std::ifstream file(expectedFilePath); + REQUIRE(file.is_open()); + + // Read the contents from the file + std::string line; + for (int i = 0; i < N / p; ++i) { + std::getline(file, line); + + std::ostringstream oss; + oss << std::setprecision(15) << locations.GetLocationX()[i] << ',' + << locations.GetLocationY()[i] << ',' << locations.GetLocationZ()[i] << ","<< std::setprecision(15) << measurements_matrix[i] ; + std::string expectedLine = oss.str(); + + REQUIRE(line == expectedLine); + } + + delete[] location_x; + delete[] location_y; + delete[] location_z; + delete[] measurements_matrix; +} +TEST_CASE("Disk Writer Tests"){ + TEST_3D_VECTORS_WRITING(); +} diff --git a/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp b/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp new file mode 100644 index 00000000..8a7f5e62 --- /dev/null +++ b/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp @@ -0,0 +1,144 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestDistanceCalculationHelpers.cpp + * @brief Unit tests for the DistanceCalculationHelpers.cpp in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the class DistanceCalculationHelpers. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-01-24 +**/ + +#include +#include + +#include +#include + +using namespace exageostat::common; +using namespace exageostat::helpers; +using namespace exageostat::dataunits; + +void TEST_RADIAN_CONVERSION() { + exageostat::helpers::DistanceCalculationHelpers distance_helper; + REQUIRE(distance_helper.DegreeToRadian(0) == Catch::Approx(0.0)); + REQUIRE(distance_helper.DegreeToRadian(90) == Catch::Approx(PI / 2)); + REQUIRE(distance_helper.DegreeToRadian(180) == Catch::Approx(PI)); +} + +void TEST_CALCULATE_DISTANCE() { + + SECTION("2D - Euclidean & Haversine Distance") { + int size = 2; + + // 2D - Locations initializations + auto *location_x = new double[size]{10.0, 4.0}; + auto *location_y = new double[size]{12.0, 4.0}; + + Locations location1(size, Dimension2D); + Locations location2(size, Dimension2D); + + location1.SetLocationX(*location_x, size); + location1.SetLocationY(*location_y, size); + location2.SetLocationX(*location_x, size); + location2.SetLocationY(*location_y, size); + + int idx1 = 0; // index of x-coordinate + int idx2 = 1; // index of y-coordinate + int flagZ = 0; // 2D case + + int distanceMetric = 0; // Default - Use Euclidean distance + REQUIRE(DistanceCalculationHelpers::CalculateDistance(location1, location2, idx1, idx2, distanceMetric, + flagZ) == 10); + + distanceMetric = 1; // Use Haversine distance + auto distance_earth = DistanceCalculationHelpers::DistanceEarth(location_x[idx1], location_y[idx1], + location_x[idx2], location_y[idx2]); + REQUIRE(DistanceCalculationHelpers::CalculateDistance(location1, location2, idx1, idx2, distanceMetric, + flagZ) == distance_earth); + + delete[] location_x; + delete[] location_y; + }SECTION("3D - Euclidean & Haversine Distance") { + int size = 3; + + // 3D - Locations initializations + auto *location_x = new double[size]{10.0, 4.0}; + auto *location_y = new double[size]{12.0, 4.0}; + auto *location_z = new double[size]{1, 2}; + + Locations location1(size, Dimension3D); + Locations location2(size, Dimension3D); + + location1.SetLocationX(*location_x, size); + location1.SetLocationY(*location_y, size); + location1.SetLocationZ(*location_z, size); + + location2.SetLocationX(*location_x, size); + location2.SetLocationY(*location_y, size); + location2.SetLocationZ(*location_z, size); + + int idx1 = 0; // index of x-coordinate + int idx2 = 1; // index of y-coordinate + int flagZ = 1; // 2D case + + int distanceMetric = 0; // Use Euclidean distance + REQUIRE(DistanceCalculationHelpers::CalculateDistance(location1, location2, idx1, idx2, distanceMetric, + flagZ) == Catch::Approx(10.05).epsilon(0.01)); + + distanceMetric = 1; // Use Haversine distance + REQUIRE_THROWS( + DistanceCalculationHelpers::CalculateDistance(location1, location2, idx1, idx2, distanceMetric, + flagZ)); + + delete[] location_x; + delete[] location_y; + delete[] location_z; + } +} + +void TEST_DISTANCE_EARTH() { + SECTION("Distance between two identical points") { + + double lat = 0.1, lon = 0.2; + REQUIRE(DistanceCalculationHelpers::DistanceEarth(lat, lon, lat, lon) == Catch::Approx(0.0)); + + }SECTION("Distance between North Pole and South Pole") { + + double north_pole_Lat = 90.0, pole_Lon = 0.0; + double south_pole_Lat = -90.0; + REQUIRE(DistanceCalculationHelpers::DistanceEarth(north_pole_Lat, pole_Lon, south_pole_Lat, pole_Lon) == + Catch::Approx(EARTH_RADIUS * M_PI)); + + }SECTION("Distance between two points on the equator 90 degrees apart") { + + double equator_Lat = 0.0; + double lon1 = 0.0, lon2 = 90.0; + REQUIRE(DistanceCalculationHelpers::DistanceEarth(equator_Lat, lon1, equator_Lat, lon2) == + Catch::Approx(EARTH_RADIUS * M_PI / 2)); + + }SECTION("Distance between two arbitrary points") { + + // Arbitrary location coordinates + double location1_lat = 40.7128; + double location1_lon = -74.0060; + double location2_lat = 34.0522; + double location2_lon = -118.2437; + + // The expected distance is approximately 3940 kilometers + double expectedDistance = 3940.0; + double calculatedDistance = DistanceCalculationHelpers::DistanceEarth(location1_lat, location1_lon, + location2_lat, location2_lon); + REQUIRE(calculatedDistance == Catch::Approx(expectedDistance).epsilon(0.01)); + + } +} + +TEST_CASE("Degree to Radian Conversion") { + TEST_RADIAN_CONVERSION(); + TEST_CALCULATE_DISTANCE(); + TEST_DISTANCE_EARTH(); +} \ No newline at end of file diff --git a/tests/cpp-tests/prediction/CMakeLists.txt b/tests/cpp-tests/prediction/CMakeLists.txt new file mode 100644 index 00000000..00fd6916 --- /dev/null +++ b/tests/cpp-tests/prediction/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2024-01-24 + +set(EXAGEOSTAT_TESTFILES + + ${CMAKE_CURRENT_SOURCE_DIR}/TestPrediction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TestPredictionHelpers.cpp + + ${EXAGEOSTAT_TESTFILES} + PARENT_SCOPE +) + diff --git a/tests/cpp-tests/prediction/TestPrediction.cpp b/tests/cpp-tests/prediction/TestPrediction.cpp new file mode 100644 index 00000000..98455d5a --- /dev/null +++ b/tests/cpp-tests/prediction/TestPrediction.cpp @@ -0,0 +1,198 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestPrediction.cpp + * @brief Unit tests for the TestPrediction class in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the TestPrediction class + * @version 1.0.0 + * @author Mahmoud ElKarargy + * @date 2024-1-18 +**/ + +#include + +#include + +using namespace std; + +using namespace exageostat::common; +using namespace exageostat::configurations; +using namespace exageostat::hardware; +using namespace exageostat::prediction; + +void TEST_PREDICTION_MISSING_DATA() { + + //Init configuration + Configurations configurations; + configurations.SetUnknownObservationsNb(4); + int N = 16; + configurations.SetProblemSize(N); + configurations.SetKernelName("UnivariateMaternStationary"); + int dts = 8; + + configurations.SetDenseTileSize(dts); + configurations.SetComputation(EXACT_DENSE); + configurations.SetMaxMleIterations(3); + configurations.SetTolerance(pow(10, -4)); + + vector lb{0.1, 0.1, 0.1}; + configurations.SetLowerBounds(lb); + configurations.SetStartingTheta(lb); + vector ub{5, 5, 5}; + configurations.SetUpperBounds(ub); + vector initial_theta{1, 0.1, 0.5}; + configurations.SetInitialTheta(initial_theta); + + + auto hardware = ExaGeoStatHardware(EXACT_DENSE, configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); + std::unique_ptr> data = std::make_unique>( + configurations.GetProblemSize(), configurations.GetDimension()); + + auto *z_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, + 0.290822066007430102}; + //creating locations x and y. + auto *location_x = new double[N]{0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, + 0.877592126344701295}; + auto *location_y = new double[N]{0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, + 0.942824444953078489}; + + data->GetLocations()->SetLocationX(*location_x, N); + data->GetLocations()->SetLocationY(*location_y, N); + + SECTION("Test Prediction - MSPE ") + { + configurations.SetIsIDW(false); + configurations.SetIsMLOEMMOM(false); + configurations.SetIsFisher(false); + configurations.SetIsMSPE(true); + + vector estimated_theta{0.9, 0.09, 0.4}; + configurations.SetEstimatedTheta(estimated_theta); + + Prediction predictor; + + // Register and create a kernel object + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); + + // Add the data prediction arguments. + configurations.InitializeDataPredictionArguments(); + predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + + REQUIRE(exageostat::results::Results::GetInstance()->GetMSPEError()== Catch::Approx(0.552448)); + delete pKernel; + } + SECTION("Test Prediction - IDW ") { + configurations.SetIsMLOEMMOM(false); + configurations.SetIsFisher(false); + configurations.SetIsMSPE(false); + configurations.SetIsIDW(true); + + vector estimated_theta{0.9, 0.09, 0.4}; + configurations.SetEstimatedTheta(estimated_theta); + + Prediction predictor; + + std::vector idw_error={ 1.18856255, 1.25725881, 1.11986628 }; + + // Register and create a kernel object + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); + // Add the data prediction arguments. + configurations.InitializeDataPredictionArguments(); + predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + for(int i =0;i<3;i++){ + REQUIRE(exageostat::results::Results::GetInstance()->GetIDWError()[i] == Catch::Approx(idw_error[i])); + } + delete pKernel; + } + SECTION("Test Prediction - MLOE_MMOM ") { + configurations.SetIsMSPE(false); + configurations.SetIsIDW(false); + configurations.SetIsMLOEMMOM(true); + configurations.SetIsFisher(false); + + vector estimated_theta{0.9, 0.09, 0.4}; + configurations.SetEstimatedTheta(estimated_theta); + + Prediction predictor; + // Register and create a kernel object + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); + // Add the data prediction arguments. + configurations.InitializeDataPredictionArguments(); + predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + REQUIRE(exageostat::results::Results::GetInstance()->GetMLOE() == Catch::Approx(0.004467).margin(0.001)); + REQUIRE(exageostat::results::Results::GetInstance()->GetMMOM() == Catch::Approx(-0.0812376).margin(0.001)); + + + vector new_estimated_theta1{1, 0.1, 0.5}; + configurations.SetEstimatedTheta(new_estimated_theta1); + // Add the data prediction arguments. + configurations.InitializeDataPredictionArguments(); + predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + REQUIRE(exageostat::results::Results::GetInstance()->GetMLOE() == Catch::Approx(0).margin(0.001)); + REQUIRE(exageostat::results::Results::GetInstance()->GetMMOM() == Catch::Approx(0).margin(0.001)); + delete pKernel; + } + + SECTION("Test Prediction - FISHER") { + configurations.SetIsMSPE(false); + configurations.SetIsIDW(false); + configurations.SetIsMLOEMMOM(false); + configurations.SetIsFisher(true); + + vector new_estimated_theta{0.9, 0.09, 0.4}; + configurations.SetEstimatedTheta(new_estimated_theta); + Prediction predictor; + // Register and create a kernel object + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); + // Add the data prediction arguments. + configurations.InitializeDataPredictionArguments(); + predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + + REQUIRE(exageostat::results::Results::GetInstance()->GetFisher00() == Catch::Approx(0.104589)); + REQUIRE(exageostat::results::Results::GetInstance()->GetFisher11() == Catch::Approx(0.187355)); + REQUIRE(exageostat::results::Results::GetInstance()->GetFisher22() == Catch::Approx(10.556483)); + delete pKernel; + } + SECTION("Test Prediction - Exception"){ + vector new_estimated_theta{-1, -1, -1}; + configurations.SetEstimatedTheta(new_estimated_theta); + Prediction predictor; + // Register and create a kernel object + configurations.SetIsMLOEMMOM(true); + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); + REQUIRE_THROWS(predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel)); + delete pKernel; + } + delete[] location_x; + delete[] location_y; + delete[] z_matrix; +} + +TEST_CASE("Test Predictions") { + TEST_PREDICTION_MISSING_DATA(); +} \ No newline at end of file diff --git a/tests/cpp-tests/helpers/TestPredictionHelpers.cpp b/tests/cpp-tests/prediction/TestPredictionHelpers.cpp similarity index 99% rename from tests/cpp-tests/helpers/TestPredictionHelpers.cpp rename to tests/cpp-tests/prediction/TestPredictionHelpers.cpp index b69b45fe..eca6c9e3 100644 --- a/tests/cpp-tests/helpers/TestPredictionHelpers.cpp +++ b/tests/cpp-tests/prediction/TestPredictionHelpers.cpp @@ -4,7 +4,7 @@ // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). /** - * @file TestPrediction.cpp + * @file TestPredictionHelpers.cpp * @brief Unit tests for the TestPrediction class in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestPrediction class * @version 1.0.0 diff --git a/tests/cpp-tests/results/CMakeLists.txt b/tests/cpp-tests/results/CMakeLists.txt new file mode 100644 index 00000000..2be16bf6 --- /dev/null +++ b/tests/cpp-tests/results/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2024-01-24 + +set(EXAGEOSTAT_TESTFILES + ${CMAKE_CURRENT_SOURCE_DIR}/TestResults.cpp + ${EXAGEOSTAT_TESTFILES} + PARENT_SCOPE +) diff --git a/tests/cpp-tests/results/TestResults.cpp b/tests/cpp-tests/results/TestResults.cpp new file mode 100644 index 00000000..72c4da1c --- /dev/null +++ b/tests/cpp-tests/results/TestResults.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestResults.cpp + * @brief Unit tests for the Results class in the ExaGeoStat software package. + * @details This file contains Catch2 unit tests that validate the functionality of the Results class + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-01-24 +**/ + +#include + +#include + +using namespace exageostat::results; + +void TEST_SINGLETON_RESULTS() { + //Test singleton instance of results + auto results1 = Results::GetInstance(); + auto results2 = Results::GetInstance(); + REQUIRE(results1 == results2); +} + +void TEST_SETTERS_AND_GETTERS() { + + auto results_instacne = Results::GetInstance(); + + SECTION("Total Modeling Execution Time Setter/Getter") { + results_instacne->SetTotalModelingExecutionTime(1.0); + REQUIRE(results_instacne->GetTotalModelingExecutionTime() == 1.0); + + }SECTION("Total Modeling Flops Setter/Getter") { + results_instacne->SetTotalModelingFlops(1.0); + REQUIRE(results_instacne->GetTotalModelingFlops() == 1.0); + + }SECTION("Avg Modeling Execution Time Setter/Getter") { + REQUIRE_THROWS(results_instacne->GetAverageModelingExecutionTime()); + + results_instacne->SetMLEIterations(2.0); + results_instacne->SetTotalModelingExecutionTime(4.0); + REQUIRE(results_instacne->GetAverageModelingExecutionTime() == 2.0); + + }SECTION("Avg Modeling Flops Setter/Getter") { + results_instacne->SetMLEIterations(0); + REQUIRE_THROWS(results_instacne->GetAverageModelingFlops()); + + results_instacne->SetMLEIterations(2.0); + results_instacne->SetTotalModelingFlops(4.0); + REQUIRE(results_instacne->GetAverageModelingFlops() == 2.0); + } +} + +TEST_CASE("Test Results") { + TEST_SINGLETON_RESULTS(); + TEST_SETTERS_AND_GETTERS(); +} From b4c6e7d8b275c4df17e84e78940943c0b1a68f71 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 12 Feb 2024 01:11:59 +0200 Subject: [PATCH 24/82] adding MakeFile --- src/Makefile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/Makefile diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 00000000..46aced6a --- /dev/null +++ b/src/Makefile @@ -0,0 +1,14 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file Makefile +# @brief Makefile for custom building ExaGeoStat +# @version 1.1.0 +# @author David Helmy +# @date 2024-01-30 + +# this file is added to let R CMD build with Custom build configuration instead of using its +# main build system +all : From f4e7e807b567ba4613262d3945c48a0fc3788d93 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 12 Feb 2024 22:26:30 +0200 Subject: [PATCH 25/82] Updated MPI --- inst/include/common/Utils.hpp | 10 +- .../include/configurations/Configurations.hpp | 4 +- inst/include/helpers/CommunicatorMPI.hpp | 10 +- .../LinearAlgebraMethods.hpp | 5 +- src/configurations/Configurations.cpp | 119 +++++++++++------- .../concrete/SyntheticGenerator.cpp | 4 +- src/hardware/ExaGeoStatHardware.cpp | 5 +- src/helpers/CommunicatorMPI.cpp | 23 ++-- .../LinearAlgebraMethods.cpp | 41 +++--- .../chameleon/ChameleonImplementation.cpp | 2 +- .../concrete/tlr/HicmaImplementation.cpp | 9 +- tests/cpp-tests/kernels/CMakeLists.txt | 4 +- .../TestUnivariatePowExpStationary.cpp | 13 +- 13 files changed, 134 insertions(+), 115 deletions(-) diff --git a/inst/include/common/Utils.hpp b/inst/include/common/Utils.hpp index f0428098..357ba341 100644 --- a/inst/include/common/Utils.hpp +++ b/inst/include/common/Utils.hpp @@ -34,7 +34,7 @@ * Verbose macro for logging and debugging mode */ #define VERBOSE(msg) \ - if(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::DETAILED_MODE && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) \ + if(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::DETAILED_MODE && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) \ std::cout << "\t\t\t " << msg << std::endl; /** @@ -42,14 +42,14 @@ */ #define LOGGER_1(msg) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) \ + if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) \ std::cout << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg << std::endl; /** * LOGGER_2 macro for logging outputs with double taps and without new line at the end. */ #define LOGGER_2(msg, A) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) \ + if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) \ std::cout << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; /** @@ -69,14 +69,14 @@ * LOGGER_PRECISION_1 macro for logging outputs without any taps, without new line at the end and with customized precision. */ #define LOGGER_PRECISION_1(msg, precision) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) \ + if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) \ std::cout << std::fixed << std::setprecision(precision) << msg; /** * LOGGER_PRECISION macro for logging outputs without any taps, without new line at the end and with default C++ precision. */ #define LOGGER_PRECISION_2(msg) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) \ + if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) \ std::cout << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; /** diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index 1c7658a4..f2e43f1e 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -78,7 +78,7 @@ namespace exageostat::configurations { * @brief destructor to allow calls to the correct concrete destructor. * */ - ~Configurations() = default; + ~Configurations(); /** * @brief Initialize the module arguments. @@ -382,7 +382,7 @@ namespace exageostat::configurations { * @brief print the summary of MLE inputs. * @return void */ - inline void PrintSummary(); + inline void PrintSummary(int aRank = 0); /** * @brief Calculates the number of observed measurements. diff --git a/inst/include/helpers/CommunicatorMPI.hpp b/inst/include/helpers/CommunicatorMPI.hpp index d9051cbb..506eb1e8 100644 --- a/inst/include/helpers/CommunicatorMPI.hpp +++ b/inst/include/helpers/CommunicatorMPI.hpp @@ -35,7 +35,7 @@ namespace exageostat::helpers { * @return The rank of the MPI process. * */ - [[nodiscard]] bool GetRank() const; + [[nodiscard]] int GetRank() ; /** * @brief Set the hardware initialization flag. @@ -55,14 +55,18 @@ namespace exageostat::helpers { private: + /** + * @brief Prevent Class Instantiation for Communicator MPI Class. + */ + CommunicatorMPI() = default; + /** * @brief Pointer to the singleton instance of the CommunicatorMPI class. * */ static CommunicatorMPI *mpInstance; - /// Used boolean to check if hardware is initialized. - bool mIsHardwareInitialized = false; + bool mIsHardwareInitialized; }; } #endif //EXAGEOSTATCPP_COMMUNICATORMPI_HPP diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp index 679acf12..150bc616 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp @@ -739,17 +739,14 @@ namespace exageostat::linearAlgebra { static void CORE_dmdet_starpu(void *apBuffers[], void *apCodeletArguments) { int m; - int n; T *pA; - int m0; - int n0; T det = 0; T *pDeterminant = &det; *pDeterminant = 0; pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); pDeterminant = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - starpu_codelet_unpack_args(apCodeletArguments, &m, &n, &m0, &n0); + starpu_codelet_unpack_args(apCodeletArguments, &m); T local_det = Core_dmdet(pA, m); *pDeterminant += local_det; } diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index 1b8ab14d..58300e3d 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -12,6 +12,10 @@ * @date 2023-01-31 **/ +#ifdef USE_MPI +#include +#endif + #include #include #include @@ -75,11 +79,20 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { this->mArgC = aArgC; this->mpArgV = apArgV; + + int rank = 0; +#ifdef USE_MPI + MPI_Init(&this->mArgC,&this->mpArgV); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); +#endif + // Get the example name string example_name = apArgV[0]; // Remove the './' example_name.erase(0, 2); - LOGGER("Running " + example_name) + if(!rank){ + LOGGER("Running " + example_name) + } string argument; string argument_name; string argument_value; @@ -216,7 +229,7 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { //initlog InitLog(); } - this->PrintSummary(); + this->PrintSummary(rank); } @@ -639,60 +652,70 @@ void Configurations::InitTheta(vector &aTheta, const int &size) { } } -void Configurations::PrintSummary() { +void Configurations::PrintSummary(int aRank) { Verbose temp = this->GetVerbosity(); mVerbosity = STANDARD_MODE; - LOGGER("********************SUMMARY**********************") - if (this->GetIsSynthetic()) { - LOGGER("#Synthetic Dataset") - } else { - LOGGER("#Real Dataset") - } - LOGGER("#Number of Locations: " << this->GetProblemSize()) - LOGGER("#Threads per node: " << this->GetCoresNumber()) - LOGGER("#GPUs: " << this->GetGPUsNumbers()) - if (this->GetPrecision() == 1) { - LOGGER("#Precision: Double") - } else if (this->GetPrecision() == 0) { - LOGGER("#Precision: Single") - } else if (this->GetPrecision() == 2) { - LOGGER("#Precision: Single/Double") - } - LOGGER("#Dense Tile Size: " << this->GetDenseTileSize()) + if (!aRank) { + + LOGGER("********************SUMMARY**********************") + if (this->GetIsSynthetic()) { + LOGGER("#Synthetic Dataset") + } else { + LOGGER("#Real Dataset") + } + LOGGER("#Number of Locations: " << this->GetProblemSize()) + LOGGER("#Threads per node: " << this->GetCoresNumber()) + LOGGER("#GPUs: " << this->GetGPUsNumbers()) + if (this->GetPrecision() == 1) { + LOGGER("#Precision: Double") + } else if (this->GetPrecision() == 0) { + LOGGER("#Precision: Single") + } else if (this->GetPrecision() == 2) { + LOGGER("#Precision: Single/Double") + } + LOGGER("#Dense Tile Size: " << this->GetDenseTileSize()) #ifdef USE_HICMA - LOGGER("#Low Tile Size: " << this->GetLowTileSize()) + LOGGER("#Low Tile Size: " << this->GetLowTileSize()) #endif - if (this->GetComputation() == TILE_LOW_RANK) { - LOGGER("#Computation: Tile Low Rank") - } else if (this->GetComputation() == EXACT_DENSE) { - LOGGER("#Computation: Exact") - } else if (this->GetComputation() == DIAGONAL_APPROX) { - LOGGER("#Computation: Diagonal Approx") - } - - if (this->GetDimension() == Dimension2D) { - LOGGER("#Dimension: 2D") - } else if (this->GetDimension() == Dimension3D) { - LOGGER("#Dimension: 3D") - } else if (this->GetDimension() == DimensionST) { - LOGGER("#Dimension: ST") - } - LOGGER("#Kernel: " << this->GetKernelName()) - if (this->GetDistanceMetric() == EUCLIDEAN_DISTANCE) { - LOGGER("#Distance Metric: Euclidean distance") - } else { - LOGGER("#Distance Metric: Great Circle Distance") - } - LOGGER("#p: " << this->GetPGrid() << "\t\t #q: " << this->GetQGrid()) - if (this->GetIsOOC()) { - LOGGER("#Out Of Core (OOC) technology is enabled") + if (this->GetComputation() == TILE_LOW_RANK) { + LOGGER("#Computation: Tile Low Rank") + } else if (this->GetComputation() == EXACT_DENSE) { + LOGGER("#Computation: Exact") + } else if (this->GetComputation() == DIAGONAL_APPROX) { + LOGGER("#Computation: Diagonal Approx") + } + + if (this->GetDimension() == Dimension2D) { + LOGGER("#Dimension: 2D") + } else if (this->GetDimension() == Dimension3D) { + LOGGER("#Dimension: 3D") + } else if (this->GetDimension() == DimensionST) { + LOGGER("#Dimension: ST") + } + LOGGER("#Kernel: " << this->GetKernelName()) + if (this->GetDistanceMetric() == EUCLIDEAN_DISTANCE) { + LOGGER("#Distance Metric: Euclidean distance") + } else { + LOGGER("#Distance Metric: Great Circle Distance") + } + LOGGER("#p: " << this->GetPGrid() << "\t\t #q: " << this->GetQGrid()) + if (this->GetIsOOC()) { + LOGGER("#Out Of Core (OOC) technology is enabled") + } + LOGGER("*************************************************") + mVerbosity = temp; } - LOGGER("*************************************************") - mVerbosity = temp; } int Configurations::CalculateZObsNumber() { return (this->GetProblemSize()) - this->GetUnknownObservationsNb(); -} \ No newline at end of file +} + +Configurations::~Configurations() { +#ifdef USE_MPI + // Finalize the MPI environment. + MPI_Finalize(); +#endif +} diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index 0ae3acd5..c3500006 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -62,10 +62,10 @@ SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aC CHAMELEON_Desc2Lap(ChamUpperLower, data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc, pMatrix, aConfigurations.GetProblemSize()); - if ( CHAMELEON_Comm_rank == 0 ){ + if (helpers::CommunicatorMPI::GetInstance()->GetRank() == 0){ helpers::DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), - aConfigurations.GetProblemSize(), aConfigurations.GetVariablesNumber(), path, + aConfigurations.GetProblemSize(), parameters_number, path, *data->GetLocations()); } delete[] pMatrix; diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 7f2c38e6..1783a7f3 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -23,7 +23,6 @@ using namespace exageostat::hardware; ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber) { - LOGGER("** Initialise ExaGeoStat hardware **") this->mComputation = aComputation; int tag_width = 31, tag_sep = 26; @@ -47,9 +46,12 @@ ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, #endif } helpers::CommunicatorMPI::GetInstance()->SetHardwareInitialization(); + LOGGER("** Initialized ExaGeoStat hardware **") } ExaGeoStatHardware::~ExaGeoStatHardware() { + + results::Results::GetInstance()->PrintEndSummary(); // finalize hardware using Chameleon if (mpChameleonContext) { CHAMELEON_Finalize() @@ -65,7 +67,6 @@ ExaGeoStatHardware::~ExaGeoStatHardware() { #endif } helpers::CommunicatorMPI::GetInstance()->RemoveHardwareInitialization(); - results::Results::GetInstance()->PrintEndSummary(); } void *ExaGeoStatHardware::GetHicmaContext() { diff --git a/src/helpers/CommunicatorMPI.cpp b/src/helpers/CommunicatorMPI.cpp index e9167c4b..31021e08 100644 --- a/src/helpers/CommunicatorMPI.cpp +++ b/src/helpers/CommunicatorMPI.cpp @@ -23,29 +23,24 @@ CommunicatorMPI *CommunicatorMPI::GetInstance() { return mpInstance; } -bool CommunicatorMPI::GetRank() const { - +int CommunicatorMPI::GetRank() { #ifdef USE_MPI - if(!this->mIsHardwareInitialized){ - return false; + if(!mIsHardwareInitialized){ + return 0; } else{ - if(CHAMELEON_Comm_rank() == 0){ - return true; - } - return false; + return CHAMELEON_Comm_rank(); } #else - return true; + return 0; #endif } void CommunicatorMPI::SetHardwareInitialization() { - this->mIsHardwareInitialized = true; + mIsHardwareInitialized = true; } - -CommunicatorMPI *CommunicatorMPI::mpInstance = nullptr; - void CommunicatorMPI::RemoveHardwareInitialization() { - this->mIsHardwareInitialized = false; + mIsHardwareInitialized = false; } + +CommunicatorMPI *CommunicatorMPI::mpInstance = nullptr; diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index 76af27ff..60956afb 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -411,11 +411,11 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig T *pMatrix; VERBOSE("Writing generated data to the disk (Synthetic Dataset Generation Phase) .....") #ifdef CHAMELEON_USE_MPI - pMatrix = new T[n]; + pMatrix = new T[full_problem_size]; string path = aConfigurations.GetLoggerPath(); - ExaGeoStatDesc2Lap(pMatrix, n, CHAM_descZ, EXAGEOSTAT_UPPER_LOWER); + ExaGeoStatDesc2Lap(pMatrix, full_problem_size, CHAM_descZ, EXAGEOSTAT_UPPER_LOWER); if ( CHAMELEON_Comm_rank() == 0 ){ - helpers::DiskWriter::WriteVectorsToDisk(*pMatrix, n, P, path, *apLocation1); + helpers::DiskWriter::WriteVectorsToDisk(*pMatrix, full_problem_size, P, path, *apLocation1); } delete[] pMatrix; #else @@ -766,28 +766,23 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< *mspe = -1; } -#if defined(CHAMELEON_USE_MPI) - if(CHAMELEON_My_Mpi_Rank() == 0) - { -#endif - if (aConfiguration.GetLogger()) { - fprintf(aConfiguration.GetFileLogPath(), - "\n\n# of missing observations :%d\n\nPrediction Execution Time: %.8f, ""Flops: %.8f, Mean Square Prediction Error (MSPE): %.8f\n\n", - aZMissNumber, (mat_gen_time + mat_gen_time_2 + time_solve + time_mspe), (flops / 1e9 / (time_solve)), - *mspe); - } - VERBOSE("- Z Actual .. Z Miss") - for (i = 0; i < aZMissNumber; i++) { - VERBOSE(" (" << apZActual[i] << ", " << apZMiss[i] << ")") - } - - results::Results::GetInstance()->SetMSPEExecutionTime(time_solve + time_gemm + time_trsm); - results::Results::GetInstance()->SetMSPEFlops((flops / 1e9 / (time_solve + time_gemm))); - results::Results::GetInstance()->SetMSPEError(*mspe); + if(helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) { + if (aConfiguration.GetLogger()) { + fprintf(aConfiguration.GetFileLogPath(), + "\n\n# of missing observations :%d\n\nPrediction Execution Time: %.8f, ""Flops: %.8f, Mean Square Prediction Error (MSPE): %.8f\n\n", + aZMissNumber, (mat_gen_time + mat_gen_time_2 + time_solve + time_mspe), + (flops / 1e9 / (time_solve)), + *mspe); + } + VERBOSE("- Z Actual .. Z Miss") + for (i = 0; i < aZMissNumber; i++) { + VERBOSE(" (" << apZActual[i] << ", " << apZMiss[i] << ")") + } -#if defined(CHAMELEON_USE_MPI) + results::Results::GetInstance()->SetMSPEExecutionTime(time_solve + time_gemm + time_trsm); + results::Results::GetInstance()->SetMSPEFlops((flops / 1e9 / (time_solve + time_gemm))); + results::Results::GetInstance()->SetMSPEError(*mspe); } -#endif T *all_mspe = new T[3]; all_mspe[0] = *mspe; diff --git a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp index 34e7af92..4c844585 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp @@ -369,7 +369,7 @@ T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardwa VERBOSE("Done.") //Distribute the values in the case of MPI -#if defined(CHAMELEON_USE_MPI) +#ifdef USE_MPI MPI_Bcast(&loglik, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD ); #endif diff --git a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp index 370d4402..2e86ec35 100644 --- a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp @@ -12,6 +12,10 @@ * @date 2023-03-26 **/ +#ifdef USE_MPI +#include +#endif + #include using namespace std; @@ -101,7 +105,7 @@ T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware & Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const Kernel &aKernel) { - this->SetContext(aHardware.GetContext(aConfigurations.GetComputation())); + this->SetContext(hardware::ExaGeoStatHardware::GetContext(aConfigurations.GetComputation())); if (!aData->GetDescriptorData()->GetIsDescriptorInitiated()) { this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(),aKernel.GetVariablesNumber(), apMeasurementsMatrix); } @@ -249,7 +253,8 @@ T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware & VERBOSE(" Done.") double global_sum; -#if defined(CHAMELEON_USE_MPI) + double local_sum = *product; +#ifdef USE_MPI MPI_Allreduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); #else global_sum = *product; diff --git a/tests/cpp-tests/kernels/CMakeLists.txt b/tests/cpp-tests/kernels/CMakeLists.txt index c6b2813b..21d77dc5 100644 --- a/tests/cpp-tests/kernels/CMakeLists.txt +++ b/tests/cpp-tests/kernels/CMakeLists.txt @@ -4,14 +4,14 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-04-29 # Automatically add all kernels tests in the concrete directory. file(GLOB ALL_KERNELS ${CMAKE_CURRENT_SOURCE_DIR}/concrete/*.cpp) -set(SOURCES +set(EXAGEOSTAT_TESTFILES ${ALL_KERNELS} ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp index 2942d703..8a63bd02 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp @@ -9,8 +9,8 @@ * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariatePowExpStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. * @version 1.0.0 - * @author Mahmoud ElKarargy * @author Sameh Abdulah + * @author Mahmoud ElKarargy * @date 2024-01-16 **/ @@ -49,15 +49,14 @@ void TEST_KERNEL_GENERATION_UnivariatePowExpStationary() { int seed = 0; srand(seed); - exageostat::dataunits::ExaGeoStatData data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + std::unique_ptr> data; + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); - auto *CHAM_descriptorZ = data.GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; + auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, + exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output - double expected_output_data[] = { -1.272336, -2.362813, 0.616384, -0.072468, 0.401498, -1.559690, 0.211848, + double expected_output_data[] = { -1.272336, -2.362813, 0.616384, -0.072468, 0.401498, -1.559690, 0.211848, 0.776627, -1.524810}; for (size_t i = 0; i < N; i++) { From 96d6ddea674d433cbe8dda066aa9ab63ad7efb09 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 12 Feb 2024 22:41:28 +0200 Subject: [PATCH 26/82] Documentation --- CONTRIBUTING.md | 3 +++ USER_MANUAL.md | 3 ++- inst/include/helpers/CommunicatorMPI.hpp | 2 +- src/helpers/CommunicatorMPI.cpp | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f1b5e5d9..f1cacec5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +7,7 @@ Contributing to ExaGeoStatCPP - [Developing new features](#developing-new-features) - [Developing bug fixes](#developing-bug-fixes) - [Creating pull requests](#creating-pull-requests) +- [Adding new Kernel](#adding-new-kernel) - [Tests](#tests) Synopsis @@ -264,6 +265,8 @@ To add a new kernel, you need to follow these steps. 3. Ensure your kernel includes all the requisite functions that adhere to the established naming conventions found in other kernels. This will allow for proper support and integration of your new kernel. +4. Test your kernel, create a test file for your kernel in tests/cpp-tests/kernels/concrete The file name should match the kernel's name. Also, The naming linkage is handled automatically, so there's no additional setup required on your part. + After finalizing your feature and confirming that your tests run successfully, you are ready to push your branch to GitHub and submit a pull request. diff --git a/USER_MANUAL.md b/USER_MANUAL.md index e9d1e456..20a6a485 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -27,7 +27,7 @@ ExaGeoStatCPP User Manual * To enable MPI, add `-m` disabled by default. * To enable verbose output, add `-v` disabled by default. * To change the installation path of the dependencies, use `-i ` project_path/installdir/_deps/ by default on Unix systems. -* To enable manually passing mkl as BLA vendor, add `-s` MKL by default. +* To enable manually passing mkl as BLA vendor, add `--use-mkl` MKL by default. * To enable packaging system for distribution, add `-p` disabled by default. ## Building @@ -76,6 +76,7 @@ Supported Covariance Functions/ Kernels: 17. bivariate_spacetime_matern_stationary 18. trivariate_matern_parsimonious +* To add your kernel, please refer to [Contribution Guidelines](CONTRIBUTING.md) ## Arguments * {Mandatory} To set the problem size (N) diff --git a/inst/include/helpers/CommunicatorMPI.hpp b/inst/include/helpers/CommunicatorMPI.hpp index 506eb1e8..3c9595eb 100644 --- a/inst/include/helpers/CommunicatorMPI.hpp +++ b/inst/include/helpers/CommunicatorMPI.hpp @@ -35,7 +35,7 @@ namespace exageostat::helpers { * @return The rank of the MPI process. * */ - [[nodiscard]] int GetRank() ; + [[nodiscard]] int GetRank() const; /** * @brief Set the hardware initialization flag. diff --git a/src/helpers/CommunicatorMPI.cpp b/src/helpers/CommunicatorMPI.cpp index 31021e08..89f371f8 100644 --- a/src/helpers/CommunicatorMPI.cpp +++ b/src/helpers/CommunicatorMPI.cpp @@ -23,7 +23,7 @@ CommunicatorMPI *CommunicatorMPI::GetInstance() { return mpInstance; } -int CommunicatorMPI::GetRank() { +int CommunicatorMPI::GetRank() const{ #ifdef USE_MPI if(!mIsHardwareInitialized){ return 0; From 9f942245f8aa367912f300a6649e3ff10e81f39f Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 13 Feb 2024 11:15:53 +0200 Subject: [PATCH 27/82] fix examples tests --- tests/heavy-tests/ExamplesTests.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/heavy-tests/ExamplesTests.cpp b/tests/heavy-tests/ExamplesTests.cpp index df7133f1..7a9774ed 100644 --- a/tests/heavy-tests/ExamplesTests.cpp +++ b/tests/heavy-tests/ExamplesTests.cpp @@ -29,7 +29,7 @@ TEST_CASE("EXAMPLES") { path currentPath = current_path().parent_path().parent_path() / "examples"; SECTION("Configuration"){ - path configurations_example = currentPath / "configurations/Example_Configurations "; + path configurations_example = currentPath / "configurations/Example_Configurations_Setup "; string arguments_string = "--N=10 --dts=8 --kernel=univariate_matern_stationary"; cout << "Running Configurations example with arguments: " + arguments_string << endl << flush; @@ -95,3 +95,4 @@ TEST_CASE("EXAMPLES") { } } } + From 47f1c1bef3e94d144c3a3cc16b07c598a30fe2ba Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 13 Feb 2024 14:26:56 +0200 Subject: [PATCH 28/82] modeling takes pointers --- .gitignore | 1 + DESCRIPTION | 2 +- cleanup | 4 - cmake/CreateSymbolTable.R | 62 -------------- configure | 2 - .../Rcpp-adapters/FunctionsAdapter.hpp | 34 +++++--- src/Rcpp-adapters/FunctionsAdapter.cpp | 83 ++++++++++++++++++- src/Rcpp-adapters/RcppModules.cpp | 4 +- src/hardware/ExaGeoStatHardware.cpp | 13 ++- tests/R-tests/TestExaGeoStat.R | 54 +++++++++--- .../Rcpp-adapters/TestAllRFunctions.cpp | 2 +- .../TestUnivariatePowExpStationary.cpp | 4 +- 12 files changed, 163 insertions(+), 102 deletions(-) delete mode 100644 cmake/CreateSymbolTable.R diff --git a/.gitignore b/.gitignore index da74638d..ba44e88c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ installdir/ .Rhistory .RData .RDataTmp +..Rcheck src/symbols.rds # tar.gz files diff --git a/DESCRIPTION b/DESCRIPTION index f69330e0..bafc2a8f 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -10,7 +10,7 @@ License: GPL (>= 3) Imports: assertthat (>= 0.2.1), MASS, methods, Rcpp (>= 1.0.9) Depends: R (>= 3.5.0), assertthat (>= 0.2.1), MASS RoxygenNote: 7.2.3 -SystemRequirements: C++ (>= 11), lapacke (https://github.com/xianyi/OpenBLAS/releases), blas (https://github.com/xianyi/OpenBLAS/releases) +SystemRequirements: C++ (>= 11), lapacke (https://github.com/xianyi/OpenBLAS/releases) NeedsCompilation: yes OS_type: unix Authors@R: c( diff --git a/cleanup b/cleanup index 9d930071..01ed4d6d 100755 --- a/cleanup +++ b/cleanup @@ -26,7 +26,3 @@ rm -rf ./doxygen_config rm -rf ./prerequisites/ rm -rf ./scripts/ rm -rf ./installdir/ - -mv ./LICENSE.md ./LICENSE -mv ./cmake/ ./src/cmake/ -mv ./CMakeLists.txt ./src/CMakeLists_inv.txt \ No newline at end of file diff --git a/cmake/CreateSymbolTable.R b/cmake/CreateSymbolTable.R deleted file mode 100644 index a8dd047a..00000000 --- a/cmake/CreateSymbolTable.R +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# @file CreateSymbolTable.R -# @version 1.1.0 -# @author David Helmy -# @date 2024-02-09 - -read_symbols_from_object_file <- function(f){ - nm <- Sys.getenv("UserNM") - if(!nzchar(nm)) { - ## reasonable to assume nm is on the path - nm <- Sys.which("nm") - if(nzchar(nm)) nm <- shQuote(nm) - } - if(!nzchar(nm)) { - warning("this requires 'nm' to be on the PATH") - return() - } - if(!(file.size(f))) return() - s <- strsplit(system(sprintf("%s -Pg %s", nm, shQuote(f)), - intern = TRUE), - " +") - ## Cannot simply rbind() this because elements may have 2-4 entries. - n <- length(s) - tab <- matrix("", nrow = n, ncol = 4L) - colnames(tab) <- c("name", "type", "value", "size") - ## Compute desired i and j positions in tab. - i <- rep.int(seq_len(n), lengths(s)) - j <- unlist(lapply(s, seq_along)) - tab[n * (j - 1L) + i] <- unlist(s) - tab -} -list_files_with_extension <- function(directory_path, file_extension) { - # Get a list of all files in the directory and its subdirectories - all_files <- list.files(directory_path, recursive = TRUE, full.names = TRUE) - - # Filter files based on the provided file extension - selected_files <- all_files[grep(paste0("\\.", file_extension, "$"), all_files, ignore.case = TRUE)] - - return(selected_files) -} - -# Define the arguments -args <- commandArgs(trailingOnly = TRUE) - -if (length(args) != 2) { - cat("\n\n\n\n") - stop("Please provide correct arguments") -} - -directory_path <- (args[1]) -file_path <- (args[2]) - -file_extension <- "o" - - -objects <- list_files_with_extension(directory_path, file_extension) -tables <- lapply(objects, read_symbols_from_object_file) -names(tables) <- objects -saveRDS(tables, file = file_path, version = 2) \ No newline at end of file diff --git a/configure b/configure index bc64f0c6..bbc31fc9 100755 --- a/configure +++ b/configure @@ -258,8 +258,6 @@ if [ "$USE_R" = "ON" ]; then cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.so" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.so -> src" fi - "${R_HOME}"/bin/Rscript "${ABSOLUTE_PATH}/cmake/CreateSymbolTable.R" "${ABSOLUTE_PATH}/bin/" "${ABSOLUTE_PATH}/src/symbols.rds" - rm -rf "${ABSOLUTE_PATH:?}/bin/"* fi diff --git a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp index 0c430048..8f74a624 100644 --- a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp +++ b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp @@ -15,6 +15,7 @@ #ifndef EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP #define EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP +#include #include #include #include @@ -74,19 +75,25 @@ namespace exageostat::adapters { */ ExaGeoStatData *R_ExaGeoStatLoadData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, ExaGeoStatData *apData); - - /** + /** * @brief Function to model ExaGeoStat data. * @details This function applies the model configurations specified in apConfigurations to the data stored in apData, using the hardware setup specified by apHardware. * It prepares the ExaGeoStatData object for statistical analysis and prediction tasks, adjusting the internal data representations and parameters as needed. * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. - * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data will be stored. - * @return A pointer to an ExaGeoStatData object containing the loaded data. - * + * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data is stored and will be modeled. + * @param[in] aMeasurementsMatrix An optional Rcpp::Nullable object containing a matrix of measurements. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsX An optional Rcpp::Nullable object containing a matrix of X coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsY An optional Rcpp::Nullable object containing a matrix of Y coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsZ An optional Rcpp::Nullable object containing a matrix of Z coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @return A pointer to an ExaGeoStatData object containing the modeled data, ready for statistical analysis and prediction. */ ExaGeoStatData *R_ExaGeoStatModelData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, - ExaGeoStatData *apData); + ExaGeoStatData *apData, + Rcpp::Nullable aMeasurementsMatrix = R_NilValue, + Rcpp::Nullable aLocationsX = R_NilValue, + Rcpp::Nullable aLocationsY = R_NilValue, + Rcpp::Nullable aLocationsZ = R_NilValue); /** * @brief Function to predict using ExaGeoStat data. @@ -94,12 +101,19 @@ namespace exageostat::adapters { * It utilizes the hardware configuration and computational settings defined by apHardware and apConfigurations, respectively, to execute prediction algorithms on the data stored in apData. * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. - * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data will be stored. - * @return A pointer to an ExaGeoStatData object containing the loaded data. - * + * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data is stored and will be modeled. + * @param[in] aMeasurementsMatrix An optional Rcpp::Nullable object containing a matrix of measurements. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsX An optional Rcpp::Nullable object containing a matrix of X coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsY An optional Rcpp::Nullable object containing a matrix of Y coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsZ An optional Rcpp::Nullable object containing a matrix of Z coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @return A pointer to an ExaGeoStatData object containing the modeled data, ready for statistical analysis and prediction. */ ExaGeoStatData *R_ExaGeoStatPredictData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, - ExaGeoStatData *apData); + ExaGeoStatData *apData, + Rcpp::Nullable aMeasurementsMatrix = R_NilValue, + Rcpp::Nullable aLocationsX = R_NilValue, + Rcpp::Nullable aLocationsY = R_NilValue, + Rcpp::Nullable aLocationsZ = R_NilValue); } #endif //EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP diff --git a/src/Rcpp-adapters/FunctionsAdapter.cpp b/src/Rcpp-adapters/FunctionsAdapter.cpp index dc025e6f..eff4c5e2 100644 --- a/src/Rcpp-adapters/FunctionsAdapter.cpp +++ b/src/Rcpp-adapters/FunctionsAdapter.cpp @@ -18,6 +18,7 @@ #include using namespace std; +using namespace Rcpp; using namespace exageostat::dataunits; using namespace exageostat::api; @@ -117,16 +118,90 @@ namespace exageostat::adapters { } ExaGeoStatData *R_ExaGeoStatModelData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, - ExaGeoStatData *apData) { + ExaGeoStatData *apData, + Nullable aMeasurementsMatrix, + Nullable aLocationsX, + Nullable aLocationsY, + Nullable aLocationsZ) { + std::unique_ptr> apDataPtr(apData); - exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(*apHardware, *apConfigurations, apDataPtr); + double* pMeasurementsMatrixPtr = nullptr; + if (aMeasurementsMatrix == nullptr || aMeasurementsMatrix.isNull()){ + // This was the only way to pass C++ tests, if we checked for aMeasurementsMatrix != nullptr a seg fault occurs + } + else{ + + NumericMatrix mat(aMeasurementsMatrix.get()); + pMeasurementsMatrixPtr = &mat[0]; // Get the raw pointer to the matrix data + + if (!aLocationsX.isNull()) { + double *pLocationsXPtr; + NumericMatrix mat_location(aLocationsX.get()); + pLocationsXPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationX(*pLocationsXPtr, mat_location.ncol()); + } + + if (aLocationsY.isNotNull()) { + double *pLocationsYPtr; + NumericMatrix mat_location(aLocationsY.get()); + pLocationsYPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationY(*pLocationsYPtr, mat_location.ncol()); + + } + + if (aLocationsZ == nullptr || aLocationsZ.isNotNull()) { + double *pLocationsZPtr; + NumericMatrix mat_location(aLocationsZ.get()); + pLocationsZPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationZ(*pLocationsZPtr, mat_location.ncol()); + } + } + + exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(*apHardware, *apConfigurations, apDataPtr, pMeasurementsMatrixPtr); return apDataPtr.release(); } ExaGeoStatData *R_ExaGeoStatPredictData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, - ExaGeoStatData *apData) { + ExaGeoStatData *apData, + Nullable aMeasurementsMatrix, + Nullable aLocationsX, + Nullable aLocationsY, + Nullable aLocationsZ) { + std::unique_ptr> apDataPtr(apData); - exageostat::api::ExaGeoStat::ExaGeoStatPrediction(*apHardware, *apConfigurations, apDataPtr); + double* pMeasurementsMatrixPtr = nullptr; + if (aMeasurementsMatrix == nullptr || aMeasurementsMatrix.isNull()){ + // This was the only way to pass C++ tests, if we checked for aMeasurementsMatrix != nullptr a seg fault occurs + } + else{ + + NumericMatrix mat(aMeasurementsMatrix.get()); + pMeasurementsMatrixPtr = &mat[0]; // Get the raw pointer to the matrix data + + if (!aLocationsX.isNull()) { + double *pLocationsXPtr; + NumericMatrix mat_location(aLocationsX.get()); + pLocationsXPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationX(*pLocationsXPtr, mat_location.ncol()); + } + + if (aLocationsY.isNotNull()) { + double *pLocationsYPtr; + NumericMatrix mat_location(aLocationsY.get()); + pLocationsYPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationY(*pLocationsYPtr, mat_location.ncol()); + + } + + if (aLocationsZ.isNotNull()) { + double *pLocationsZPtr; + NumericMatrix mat_location(aLocationsZ.get()); + pLocationsZPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationZ(*pLocationsZPtr, mat_location.ncol()); + } + } + + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(*apHardware, *apConfigurations, apDataPtr, pMeasurementsMatrixPtr); return apDataPtr.release(); } } \ No newline at end of file diff --git a/src/Rcpp-adapters/RcppModules.cpp b/src/Rcpp-adapters/RcppModules.cpp index d7636283..b438127f 100644 --- a/src/Rcpp-adapters/RcppModules.cpp +++ b/src/Rcpp-adapters/RcppModules.cpp @@ -54,6 +54,6 @@ RCPP_MODULE(ExaGeoStatCPP) { _["mle_itr"] = 0, _["tol"] = 4, _["prediction"] = IntegerVector::create(0, 0, 0, 0, 0))); function("simulate_data", &exageostat::adapters::R_ExaGeoStatLoadData, List::create(_["hardware"], _["config"], _["data"])); - function("model_data", &exageostat::adapters::R_ExaGeoStatModelData, List::create(_["hardware"], _["config"], _["data"])); - function("predict_data", &exageostat::adapters::R_ExaGeoStatPredictData, List::create(_["hardware"], _["config"], _["data"])); + function("model_data", &exageostat::adapters::R_ExaGeoStatModelData, List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, _["y"] = R_NilValue, _["z"] = R_NilValue)); + function("predict_data", &exageostat::adapters::R_ExaGeoStatPredictData, List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, _["y"] = R_NilValue, _["z"] = R_NilValue)); } diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 53b3b326..7beb2bb2 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -61,7 +61,18 @@ void ExaGeoStatHardware::InitHardware(const Computation &aComputation, const int } void ExaGeoStatHardware::FinalizeHardware(){ - this->~ExaGeoStatHardware(); + // finalize hardware using Chameleon + if (mpChameleonContext) { + CHAMELEON_Finalize() + mpChameleonContext = nullptr; + } + // finalize hardware using HiCMA +#ifdef USE_HICMA + if (mpHicmaContext) { + HICMA_Finalize(); + mpHicmaContext = nullptr; + } +#endif } ExaGeoStatHardware::~ExaGeoStatHardware() { diff --git a/tests/R-tests/TestExaGeoStat.R b/tests/R-tests/TestExaGeoStat.R index ba1d5ab0..25042bff 100644 --- a/tests/R-tests/TestExaGeoStat.R +++ b/tests/R-tests/TestExaGeoStat.R @@ -22,7 +22,7 @@ lts <- 0 computation <- "exact" hardware <- new(Hardware, computation, ncores, ngpus) -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=10, prediction=c(5,1,1,1,1)) +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1)) data_source <- new(Data, problem_size, "2D") exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) @@ -31,23 +31,51 @@ predict_data(hardware=hardware, config=config, data=exageostat_data) paste("---------------------------------------------------------------") hardware$finalize_hardware() -paste("ExaGeoStat with data generation and Modeling only") - -problem_size <- 10 +paste("ExaGeoStat with data Modeling only") ncores <- 5 hardware <- new(Hardware, computation, ncores, ngpus) -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5) -data_source <- new(Data, problem_size, "2D") -exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) -model_data(hardware=hardware, config=config, data=exageostat_data) +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=10) +exageostat_data <- new(Data, problem_size, "2D") +numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, + 0.290822066007430102) +z_value = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + +numbers <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, + 0.877592126344701295) +locations_x = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + +numbers <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, + 0.942824444953078489) +locations_y = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) +model_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) paste("---------------------------------------------------------------") -paste("ExaGeoStat with data generation and Prediction using MLOE_MMOM only") +paste("ExaGeoStat with data Prediction only - using mspe only") -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(1,0.1,0.3), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,0,0,1)) -data_source <- new(Data, problem_size, "2D") -exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) -predict_data(hardware=hardware, config=config, data=exageostat_data) +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,1,0,0,0)) +predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) + +paste("---------------------------------------------------------------") +paste("ExaGeoStat with data Prediction only - using idw only") + +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,1,0,0)) +predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) +paste("---------------------------------------------------------------") +paste("ExaGeoStat with data Prediction only - using MLOE_MMOM only") +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,0,0,1)) +predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) diff --git a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp index 36053745..fead63b2 100644 --- a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp +++ b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp @@ -28,7 +28,7 @@ void TEST_ALL_R_METHODS() { auto configurations = R_InitializeArguments(16, "univariate_matern_stationary", {8, 0}, {1, 1}, 1, "exact", "double", {1, 0}, 0, 1, {1,0.1,0.5}, { {0.1,0.1,0.1}, {5,5,5}}, {-1,-1,-1}, "standard", "2D", 10, 4, {5,1,1,1,1}); auto hardware = ExaGeoStatHardware("exact", 1, 0); - auto data_source = new ExaGeoStatData(10, "2D"); + auto data_source = new ExaGeoStatData(16, "2D"); auto exageostat_data = R_ExaGeoStatLoadData(&hardware, configurations, data_source); R_ExaGeoStatModelData(&hardware, configurations, exageostat_data); R_ExaGeoStatPredictData(&hardware, configurations, exageostat_data); diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp index bab306b3..8d85d283 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp @@ -51,10 +51,10 @@ void TEST_KERNEL_GENERATION_UnivariatePowExpStationary() { exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; + exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output - double expected_output_data[] = { -1.272336, -2.362813, 0.616384, -0.072468, 0.401498, -1.559690, 0.211848, + double expected_output_data[] = { -1.272336, -2.362813, 0.616384, -0.072468, 0.401498, -1.559690, 0.211848, 0.776627, -1.524810}; for (size_t i = 0; i < N; i++) { From e308bafc6ae8949fe06a19331f9cb3113d3ccf4b Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 20 Feb 2024 00:51:47 +0200 Subject: [PATCH 29/82] example for read and write data --- CMakeLists.txt | 5 - CONTRIBUTING.md | 3 + USER_MANUAL.md | 3 +- examples/CMakeLists.txt | 4 +- examples/data-loader/CMakeLists.txt | 15 +++ examples/data-loader/CSVLoader.cpp | 68 ++++++++++ examples/descriptors/CMakeLists.txt | 23 ++++ examples/descriptors/ChameleonDescriptor.cpp | 93 ++++++++++++++ .../descriptors/ChameleonToHicmaConverter.cpp | 120 ++++++++++++++++++ examples/descriptors/HicmaDescriptor.cpp | 98 ++++++++++++++ .../Rcpp-adapters/FunctionsAdapter.hpp | 2 +- inst/include/common/Definitions.hpp | 15 ++- inst/include/common/PluginRegistry.hpp | 2 +- .../include/configurations/Configurations.hpp | 11 +- inst/include/helpers/CommunicatorMPI.hpp | 10 +- .../LinearAlgebraMethods.hpp | 5 +- inst/include/utilities/Logger.hpp | 10 +- src/Rcpp-adapters/FunctionsAdapter.cpp | 18 ++- src/Rcpp-adapters/RcppModules.cpp | 25 ++-- src/configurations/Configurations.cpp | 114 ++++++++++------- .../concrete/SyntheticGenerator.cpp | 4 +- src/data-loader/concrete/CSVLoader.cpp | 1 + src/hardware/ExaGeoStatHardware.cpp | 3 +- src/helpers/CommunicatorMPI.cpp | 23 ++-- .../LinearAlgebraMethods.cpp | 41 +++--- .../chameleon/ChameleonImplementation.cpp | 2 +- .../concrete/tlr/HicmaImplementation.cpp | 9 +- tests/R-tests/TestExaGeoStat.R | 56 +++++--- .../Rcpp-adapters/TestAllRFunctions.cpp | 7 +- tests/heavy-tests/ExamplesTests.cpp | 2 +- 30 files changed, 638 insertions(+), 154 deletions(-) create mode 100644 examples/data-loader/CMakeLists.txt create mode 100644 examples/data-loader/CSVLoader.cpp create mode 100644 examples/descriptors/CMakeLists.txt create mode 100644 examples/descriptors/ChameleonDescriptor.cpp create mode 100644 examples/descriptors/ChameleonToHicmaConverter.cpp create mode 100644 examples/descriptors/HicmaDescriptor.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f75fc822..ff95aa91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,11 +62,6 @@ message(STATUS "CMAKE VERSION: ${CMAKE_VERSION}") enable_language(CXX) # Get the current path of the project. add_compile_definitions(PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}/") -# Add Paths definitions -add_definitions( - -DLOG_PATH="${PROJECT_SOURCE_DIR}/synthetic_ds/" - -DKERNELS_PATH="${PROJECT_SOURCE_DIR}/inst/include/kernels/concrete/" -) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f1b5e5d9..f1cacec5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +7,7 @@ Contributing to ExaGeoStatCPP - [Developing new features](#developing-new-features) - [Developing bug fixes](#developing-bug-fixes) - [Creating pull requests](#creating-pull-requests) +- [Adding new Kernel](#adding-new-kernel) - [Tests](#tests) Synopsis @@ -264,6 +265,8 @@ To add a new kernel, you need to follow these steps. 3. Ensure your kernel includes all the requisite functions that adhere to the established naming conventions found in other kernels. This will allow for proper support and integration of your new kernel. +4. Test your kernel, create a test file for your kernel in tests/cpp-tests/kernels/concrete The file name should match the kernel's name. Also, The naming linkage is handled automatically, so there's no additional setup required on your part. + After finalizing your feature and confirming that your tests run successfully, you are ready to push your branch to GitHub and submit a pull request. diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 7f95fd8e..f0e6b849 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -27,7 +27,7 @@ ExaGeoStatCPP User Manual * To enable MPI, add `-m` disabled by default. * To enable verbose output, add `-v` disabled by default. * To change the installation path of the dependencies, use `-i ` project_path/installdir/_deps/ by default on Unix systems. -* To enable manually passing mkl as BLA vendor, add `-s` MKL by default. +* To enable manually passing mkl as BLA vendor, add `--use-mkl` MKL by default. * To enable packaging system for distribution, add `-p` disabled by default. ## Building @@ -76,6 +76,7 @@ Supported Covariance Functions/ Kernels: 17. bivariate_spacetime_matern_stationary 18. trivariate_matern_parsimonious +* To add your kernel, please refer to [Contribution Guidelines](CONTRIBUTING.md) ## Arguments * {Mandatory} To set the problem size (N) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 661d1098..6166537d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -7,9 +7,11 @@ # @brief Includes subdirectories for different modules of the ExaGeoStat software package. # @version 1.1.0 # @author Mahmoud ElKarargy -# @date 2023-01-31 +# @date 2024-02-24 # Include subdirectories for end-to-end module, configurations module and data-generators module. add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/end-to-end) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/configurations) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-generators) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-loader) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/descriptors) diff --git a/examples/data-loader/CMakeLists.txt b/examples/data-loader/CMakeLists.txt new file mode 100644 index 00000000..dc85167c --- /dev/null +++ b/examples/data-loader/CMakeLists.txt @@ -0,0 +1,15 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2024-02-14 + +# Define the target executable +add_executable(Example_CSV_Loader ${CMAKE_CURRENT_SOURCE_DIR}/CSVLoader.cpp) + +# Link the target executable with the project and any additional libraries +target_link_libraries(Example_CSV_Loader PRIVATE ${PROJECT_NAME}_INTERFACE) diff --git a/examples/data-loader/CSVLoader.cpp b/examples/data-loader/CSVLoader.cpp new file mode 100644 index 00000000..47005a63 --- /dev/null +++ b/examples/data-loader/CSVLoader.cpp @@ -0,0 +1,68 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file CSVLoader.cpp + * @brief Example of the CSVLoader class + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-02-18 +**/ + +#include +#include + +using namespace std; + +using namespace exageostat::kernels; +using namespace exageostat::common; +using namespace exageostat::generators; +using namespace exageostat::dataLoader::csv; + +int main(int argc, char **argv) { + LOGGER("** Example of CSV Loader **") + + // Create and Initialize a new configurations object. + Configurations configurations; + configurations.InitializeArguments(argc, argv); + + // Generate Data and Log it into file + configurations.SetLogger(true); + configurations.InitializeDataGenerationArguments(); + + // Initialize ExaGeoStat Hardware and Kernel. + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), + configurations.GetGPUsNumbers()); + + Kernel *kernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), configurations.GetTimeSlot()); + int kernel_variables = kernel->GetVariablesNumber(); + + // Create a unique pointer to a DataGenerator object and generate data + configurations.SetIsSynthetic(true); + unique_ptr> synthetic_generator = DataGenerator::CreateGenerator(configurations); + auto data = synthetic_generator->CreateData(configurations, hardware, *kernel); + + // Read csv file using data loader + vector measurements_vector; + vector x_locations; + vector y_locations; + vector z_locations; + + auto loader = CSVLoader::GetInstance(); + loader->ReadData(configurations, measurements_vector, x_locations, y_locations, z_locations, kernel_variables); + + // Print loaded data + LOGGER("Data Loaded:") + for (int i=0;i +#include +#include +#include +#include + +using namespace std; + +using namespace exageostat::common; +using namespace exageostat::kernels; +using namespace exageostat::plugins; +using namespace exageostat::dataunits; + +int main(int argc, char **argv) { + + LOGGER("** Example of Chameleon Descriptor **") + + // Initialize Configuration + Configurations configuration; + configuration.InitializeArguments(argc, argv); + + // Initialize Hardware and Data + auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), + configuration.GetGPUsNumbers()); + unique_ptr> data = make_unique>(); + Kernel *kernel = PluginRegistry>::Create( + configuration.GetKernelName(), configuration.GetTimeSlot()); + + // Get arguments for Descriptors Initialization + int kernel_variables_number = kernel->GetVariablesNumber(); + int config_problem_size = configuration.GetProblemSize(); + int config_full_problem_size = config_problem_size * kernel_variables_number; + int config_dts = configuration.GetDenseTileSize(); + int config_p_grid = configuration.GetPGrid(); + int config_q_grid = configuration.GetQGrid(); + bool config_is_OOC = configuration.GetIsOOC(); + + // Randomly Initialized Matrix of Data + std::vector matrix(config_problem_size * config_problem_size); + for (int i = 0; i < config_problem_size; ++i) { + matrix[i] = i; + } + + // Set Data Descriptor + data->SetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C, config_is_OOC, matrix.data(), EXAGEOSTAT_REAL_DOUBLE, + config_dts, + config_dts, + config_dts * config_dts, config_full_problem_size, config_full_problem_size, 0, 0, + config_full_problem_size, + config_full_problem_size, config_p_grid, config_q_grid); + + auto *CHAM_descriptorC = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; + + //Print Descriptor Parameters + LOGGER("** Descriptor Parameters:") + LOGGER(" Problem Size: " << CHAM_descriptorC->m) + LOGGER(" Dense Tile Size: " << CHAM_descriptorC->mb) + + LOGGER(" Entire Number of Rows :" << CHAM_descriptorC->lm) + LOGGER(" Entire Number of Columns :" << CHAM_descriptorC->ln) + + LOGGER(" Number of Sub-matrix Tile Rows: " << CHAM_descriptorC->mt) + LOGGER(" Number of Sub-matrix Tile Columns: " << CHAM_descriptorC->nt) + + LOGGER(" Number of Rows of 2D distribution grid: " << CHAM_descriptorC->p) + LOGGER(" Number of Rows of 2D distribution grid: " << CHAM_descriptorC->q) + + LOGGER(" Is Matrix Not Fit in Memory: " << CHAM_descriptorC->ooc) + LOGGER(" Size including Padding: " << CHAM_descriptorC->bsiz) + + // Print Data Matrix of Descriptor + LOGGER("** Data in Matrix:") + LOGGER_2("",0) + auto *data_mat = (double *) CHAM_descriptorC->mat; + for (int i = 0; i < config_problem_size; ++i) { + LOGGER_PRECISION_1(" " << data_mat[i],0) + } + + delete kernel; +} diff --git a/examples/descriptors/ChameleonToHicmaConverter.cpp b/examples/descriptors/ChameleonToHicmaConverter.cpp new file mode 100644 index 00000000..03a1cfc9 --- /dev/null +++ b/examples/descriptors/ChameleonToHicmaConverter.cpp @@ -0,0 +1,120 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ChameleonDescriptor.cpp + * @brief Example file for the Chameleon to Hicma Converter. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-02-14 +**/ + +#include +#include +#include +#include +#include + +using namespace std; + +using namespace exageostat::common; +using namespace exageostat::kernels; +using namespace exageostat::plugins; +using namespace exageostat::dataunits; + +int main(int argc, char **argv) { + + LOGGER("** Example of Chameleon To Hicma Converter **") + + // Initialize Configuration + Configurations configuration; + configuration.InitializeArguments(argc, argv); + + // Initialize Hardware and Data + auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), + configuration.GetGPUsNumbers()); + unique_ptr> data = make_unique>(); + Kernel *kernel = PluginRegistry>::Create( + configuration.GetKernelName(), configuration.GetTimeSlot()); + + // Get arguments for Descriptors Initialization + int kernel_variables_number = kernel->GetVariablesNumber(); + int config_problem_size = configuration.GetProblemSize(); + int config_full_problem_size = config_problem_size * kernel_variables_number; + int config_dts = configuration.GetDenseTileSize(); + int config_p_grid = configuration.GetPGrid(); + int config_q_grid = configuration.GetQGrid(); + bool config_is_OOC = configuration.GetIsOOC(); + + // Randomly Initialized Matrix of Data + std::vector matrix(config_problem_size); + for (int i = 0; i < config_problem_size; ++i) { + matrix[i] = i; + } + + // Set Data Descriptor + data->SetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C, config_is_OOC, matrix.data(), EXAGEOSTAT_REAL_DOUBLE, + config_dts, + config_dts, + config_dts * config_dts, config_full_problem_size, config_full_problem_size, 0, 0, + config_full_problem_size, + config_full_problem_size, config_p_grid, config_q_grid); + + auto *CHAM_descriptorC = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; + + //Print Descriptor Attributes Before and After conversion + LOGGER("** Chameleon Descriptor, Before Conversion:") + LOGGER(" Problem Size: " << CHAM_descriptorC->m) + LOGGER(" Dense Tile Size: " << CHAM_descriptorC->mb) + + LOGGER(" Entire Number of Rows :" << CHAM_descriptorC->lm) + LOGGER(" Entire Number of Columns :" << CHAM_descriptorC->ln) + + LOGGER(" Number of Sub-matrix Tile Rows: " << CHAM_descriptorC->mt) + LOGGER(" Number of Sub-matrix Tile Columns: " << CHAM_descriptorC->nt) + + LOGGER(" Number of Rows of 2D distribution grid: " << CHAM_descriptorC->p) + LOGGER(" Number of Rows of 2D distribution grid: " << CHAM_descriptorC->q) + + LOGGER(" Is Matrix Not Fit in Memory: " << CHAM_descriptorC->ooc) + LOGGER(" Size including Padding: " << CHAM_descriptorC->bsiz) + + // Print Data Matrix of Descriptor + LOGGER("** Data in Matrix of Chameleon descriptor:") + LOGGER_2("",0) + auto *cham_mat = (double *) CHAM_descriptorC->mat; + for (int i = 0; i < config_problem_size; ++i) { + LOGGER_PRECISION_1(" " << cham_mat[i],0) + } + + auto *HICMA_descriptorC = data->ConvertChameleonToHicma(CHAM_descriptorC); + + LOGGER("\n\t\t ** Hicma Descriptor, After Conversion:") + LOGGER(" Problem Size: " << HICMA_descriptorC->m) + LOGGER(" Dense Tile Size: " << HICMA_descriptorC->mb) + + LOGGER(" Entire Number of Rows :" << HICMA_descriptorC->lm) + LOGGER(" Entire Number of Columns :" << HICMA_descriptorC->ln) + + LOGGER(" Number of Sub-matrix Tile Rows: " << HICMA_descriptorC->mt) + LOGGER(" Number of Sub-matrix Tile Columns: " << HICMA_descriptorC->nt) + + LOGGER(" Number of Rows of 2D distribution grid: " << HICMA_descriptorC->p) + LOGGER(" Number of Rows of 2D distribution grid: " << HICMA_descriptorC->q) + + LOGGER(" Is Matrix Not Fit in Memory: " << HICMA_descriptorC->ooc) + LOGGER(" Size including Padding: " << HICMA_descriptorC->bsiz) + + // Print Data Matrix of Descriptor + LOGGER("** Data in Matrix of Hicma descriptor:") + auto *hicma_mat = (double *) HICMA_descriptorC->mat; + LOGGER_2("",0) + for (int i = 0; i < config_problem_size; ++i) { + LOGGER_PRECISION_1(" " << hicma_mat[i],0) + } + + delete kernel; + delete HICMA_descriptorC; +} diff --git a/examples/descriptors/HicmaDescriptor.cpp b/examples/descriptors/HicmaDescriptor.cpp new file mode 100644 index 00000000..169dffac --- /dev/null +++ b/examples/descriptors/HicmaDescriptor.cpp @@ -0,0 +1,98 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file HicmaDescriptor.cpp + * @brief Example file for the Hicma descriptor. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-02-14 +**/ + +#include +#include +#include +#include +#include + +using namespace std; + +using namespace exageostat::common; +using namespace exageostat::kernels; +using namespace exageostat::plugins; +using namespace exageostat::dataunits; + +int main(int argc, char **argv) { + + LOGGER("** Example of Hicma Descriptor **") + + // Initialize Synthetic Configuration + Configurations configuration; + configuration.InitializeArguments(argc, argv); + + //Check for TLR computation specific for HICMA descriptor + if (configuration.GetComputation() != TILE_LOW_RANK) { + LOGGER("You must provide TILE_LOW_RANK computation to initialize HICMA descriptor.") + LOGGER("Consider adding \"--computation=tlr\" to the arguments") + return 0; + } + // Initialize Hardware and Data + auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), + configuration.GetGPUsNumbers()); + unique_ptr> data = make_unique>(); + Kernel *kernel = PluginRegistry>::Create( + configuration.GetKernelName(), configuration.GetTimeSlot()); + + // Get arguments for Descriptors Initialization + int kernel_variables_number = kernel->GetVariablesNumber(); + int config_problem_size = configuration.GetProblemSize(); + int config_full_problem_size = config_problem_size * kernel_variables_number; + int config_dts = configuration.GetDenseTileSize(); + int config_p_grid = configuration.GetPGrid(); + int config_q_grid = configuration.GetQGrid(); + bool config_is_OOC = configuration.GetIsOOC(); + + // Randomly Initialized Matrix of Data + std::vector matrix(config_problem_size * config_problem_size); + for (int i = 0; i < config_problem_size; ++i) { + matrix[i] = i; + } + + // Set Data Descriptor + data->SetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_C, config_is_OOC, matrix.data(), EXAGEOSTAT_REAL_DOUBLE, + config_dts, + config_dts, + config_dts * config_dts, config_full_problem_size, config_full_problem_size, 0, 0, + config_full_problem_size, + config_full_problem_size, config_p_grid, config_q_grid); + + auto *HICMA_descriptorC = data->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_C).hicma_desc; + + //Print Descriptor Attributes + LOGGER(" Problem Size: " << HICMA_descriptorC->m) + LOGGER(" Dense Tile Size: " << HICMA_descriptorC->mb) + + LOGGER(" Entire Number of Rows :" << HICMA_descriptorC->lm) + LOGGER(" Entire Number of Columns :" << HICMA_descriptorC->ln) + + LOGGER(" Number of Sub-matrix Tile Rows: " << HICMA_descriptorC->mt) + LOGGER(" Number of Sub-matrix Tile Columns: " << HICMA_descriptorC->nt) + + LOGGER(" Number of Rows of 2D distribution grid: " << HICMA_descriptorC->p) + LOGGER(" Number of Rows of 2D distribution grid: " << HICMA_descriptorC->q) + + LOGGER(" Is Matrix Not Fit in Memory: " << HICMA_descriptorC->ooc) + LOGGER(" Size including Padding: " << HICMA_descriptorC->bsiz) + + // Print Data Matrix of Descriptor + LOGGER("** Data in Matrix:") + LOGGER_2("", 0) + auto *data_mat = (double *) HICMA_descriptorC->mat; + for (int i = 0; i < config_problem_size; ++i) { + LOGGER_PRECISION_1(" " << data_mat[i], 0) + } + + delete kernel; +} diff --git a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp index 8f74a624..73c43d27 100644 --- a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp +++ b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp @@ -60,7 +60,7 @@ namespace exageostat::adapters { const std::vector> &aLowerUpperBounds, const std::vector &aEstimatedTheta, const std::string &aVerbose, const std::string &aDimension, const int &aMaxMleIterations, const double &aTolerance, - const std::vector &aPrediction + const std::vector &aPrediction, const std::vector &aPath, const bool &aSaveData ); /** diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index 7944b751..65e91a68 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -14,11 +14,10 @@ * @date 2023-03-21 **/ - #ifndef EXAGEOSTATCPP_DEFINITIONS_HPP #define EXAGEOSTATCPP_DEFINITIONS_HPP -//// TODO: This is a hot fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. +// This is a hot fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. #ifdef min #undef min #endif @@ -57,6 +56,16 @@ */ #define Q_NORM 1.959964 +/** + * Kernel Files Path Definition + */ +#define KERNELS_PATH PROJECT_SOURCE_DIR "/inst/include/kernels/concrete/" + +/** + * Logging Path Definition + */ +#define LOG_PATH PROJECT_SOURCE_DIR "/synthetic_ds/" + namespace exageostat::common { /** @@ -281,8 +290,6 @@ namespace exageostat::common { // This set stores the kernel names. std::set kernelNames; // This string stores the directory path where the kernel files are located. - // The path is obtained by using the __FILE__ macro to get the full path of the current source file, - // and then navigating two levels up to reach the directory that contains the kernel files. const std::string directoryPath = KERNELS_PATH; // This loop iterates through all the files in the directory and extracts the kernel names. for (const auto &entry: std::filesystem::directory_iterator(directoryPath)) { diff --git a/inst/include/common/PluginRegistry.hpp b/inst/include/common/PluginRegistry.hpp index dcf14845..f91c8fc1 100644 --- a/inst/include/common/PluginRegistry.hpp +++ b/inst/include/common/PluginRegistry.hpp @@ -14,7 +14,7 @@ #ifndef EXAGEOSTATCPP_PLUGINREGISTRY_HPP #define EXAGEOSTATCPP_PLUGINREGISTRY_HPP -//// TODO: This is a hot fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. +// This is a hot fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. #ifdef min #undef min #endif diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index 75a7c9f4..ccf8932a 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -370,11 +370,12 @@ class Configurations { */ static void InitTheta(std::vector &aTheta, const int &aSize); - /** - * @brief print the summary of MLE inputs. - * @return void - */ - inline void PrintSummary(); + /** + * @brief print the summary of MLE inputs. + * @param[in] aRank A MPI Rank variable + * @return void + */ + inline void PrintSummary(int aRank = 0); /** * @brief Calculates the number of observed measurements. diff --git a/inst/include/helpers/CommunicatorMPI.hpp b/inst/include/helpers/CommunicatorMPI.hpp index 9e4160ea..23162c58 100644 --- a/inst/include/helpers/CommunicatorMPI.hpp +++ b/inst/include/helpers/CommunicatorMPI.hpp @@ -35,7 +35,7 @@ namespace exageostat::helpers { * @return The rank of the MPI process. * */ - [[nodiscard]] bool GetRank() const; + [[nodiscard]] int GetRank() const; /** * @brief Set the hardware initialization flag. @@ -55,14 +55,18 @@ namespace exageostat::helpers { private: + /** + * @brief Prevent Class Instantiation for Communicator MPI Class. + */ + CommunicatorMPI() = default; + /** * @brief Pointer to the singleton instance of the CommunicatorMPI class. * */ static CommunicatorMPI *mpInstance; - /// Used boolean to check if hardware is initialized. - bool mIsHardwareInitialized = false; + bool mIsHardwareInitialized; }; } #endif //EXAGEOSTATCPP_COMMUNICATORMPI_HPP diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp index 86010997..85d57aad 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp @@ -739,17 +739,14 @@ namespace exageostat::linearAlgebra { static void CORE_dmdet_starpu(void *apBuffers[], void *apCodeletArguments) { int m; - int n; T *pA; - int m0; - int n0; T det = 0; T *pDeterminant = &det; *pDeterminant = 0; pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); pDeterminant = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - starpu_codelet_unpack_args(apCodeletArguments, &m, &n, &m0, &n0); + starpu_codelet_unpack_args(apCodeletArguments, &m); T local_det = Core_dmdet(pA, m); *pDeterminant += local_det; } diff --git a/inst/include/utilities/Logger.hpp b/inst/include/utilities/Logger.hpp index 967b039b..ae9aacb3 100644 --- a/inst/include/utilities/Logger.hpp +++ b/inst/include/utilities/Logger.hpp @@ -38,7 +38,7 @@ */ #define VERBOSE(msg) \ if(Configurations::GetVerbosity() == exageostat::common::Verbose::DETAILED_MODE && \ - exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) { \ + !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) { \ std::ostringstream oss; \ oss << "\t\t\t " << msg << std::endl; \ EXAGEOSTAT_PRINTER(oss.str()); \ @@ -49,7 +49,7 @@ * @brief LOGGER_1 macro for logging outputs with double taps and new line at the end. */ #define LOGGER_1(msg) \ - if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ std::ostringstream oss; \ oss << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg << std::endl; \ EXAGEOSTAT_PRINTER(oss.str()); \ @@ -60,7 +60,7 @@ * @brief LOGGER_2 macro for logging outputs with double taps and without new line at the end. */ #define LOGGER_2(msg, A) \ - if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ std::ostringstream oss; \ oss << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; \ EXAGEOSTAT_PRINTER(oss.str()); \ @@ -85,7 +85,7 @@ * @brief LOGGER_PRECISION_1 macro for logging outputs without any taps, without new line at the end, and with customized precision. */ #define LOGGER_PRECISION_1(msg, precision) \ - if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ std::ostringstream oss; \ oss << std::fixed << std::setprecision(precision) << msg; \ EXAGEOSTAT_PRINTER(oss.str()); \ @@ -96,7 +96,7 @@ * @brief LOGGER_PRECISION_2 macro for logging outputs without any taps, without new line at the end, and with default C++ precision. */ #define LOGGER_PRECISION_2(msg) \ - if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) {\ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) {\ std::ostringstream oss; \ oss << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; \ EXAGEOSTAT_PRINTER(oss.str()); \ diff --git a/src/Rcpp-adapters/FunctionsAdapter.cpp b/src/Rcpp-adapters/FunctionsAdapter.cpp index eff4c5e2..f0c52feb 100644 --- a/src/Rcpp-adapters/FunctionsAdapter.cpp +++ b/src/Rcpp-adapters/FunctionsAdapter.cpp @@ -32,7 +32,7 @@ namespace exageostat::adapters { const int &aMaxRank, const vector &aInitialTheta, const vector > &aLowerUpperBounds, const vector &aEstimatedTheta, const string &aVerbose, const string &aDimension, const int &aMaxMleIterations, - const double &aTolerance, const vector &aPrediction) { + const double &aTolerance, const vector &aPrediction, const std::vector &aPath, const bool &aSaveData) { vector argStrings; @@ -66,7 +66,21 @@ namespace exageostat::adapters { argStrings.push_back("--dimension=" + aDimension); argStrings.push_back("--max_mle_iterations=" + to_string(aMaxMleIterations)); argStrings.push_back("--tolerance=" + to_string(aTolerance)); - + if(!aPath[0].empty()){ + argStrings.push_back("--log_path=" + aPath[0]); + } + if(!aPath[1].empty()){ + argStrings.push_back("--data_path=" + aPath[1]); + } + if(!aPath[2].empty()){ + argStrings.push_back("--observations_file=" + aPath[2]); + } + if(!aPath[3].empty()){ + argStrings.push_back("--recovery_file=" + aPath[3]); + } + if(aSaveData){ + argStrings.emplace_back("--log"); + } // This means that ZMiss > 0, which means prediction is activated if (aPrediction[0] > 0) { argStrings.push_back("--ZMiss=" + to_string(aPrediction[0])); diff --git a/src/Rcpp-adapters/RcppModules.cpp b/src/Rcpp-adapters/RcppModules.cpp index b438127f..ed22883f 100644 --- a/src/Rcpp-adapters/RcppModules.cpp +++ b/src/Rcpp-adapters/RcppModules.cpp @@ -47,13 +47,20 @@ RCPP_MODULE(ExaGeoStatCPP) { /** Configurations Function **/ function("configurations_init", &exageostat::adapters::R_InitializeArguments, - List::create(_["n"], _["kernel"], _["tile_size"], _["p_q"] = IntegerVector::create(1, 1), _["time_slot"] = 1, - _["computation"] = "exact", _["precision"] = "double", _["cores_gpus"] = IntegerVector::create(1, 0), - _["band"] = 1, _["max_rank"] = 500, _["iTheta"], _["lb_ub"], - _["eTheta"]=IntegerVector::create(-1,-1,-1), _["verbose"] = "standard", _["dimension"] = "2D", - _["mle_itr"] = 0, _["tol"] = 4, _["prediction"] = IntegerVector::create(0, 0, 0, 0, 0))); - - function("simulate_data", &exageostat::adapters::R_ExaGeoStatLoadData, List::create(_["hardware"], _["config"], _["data"])); - function("model_data", &exageostat::adapters::R_ExaGeoStatModelData, List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, _["y"] = R_NilValue, _["z"] = R_NilValue)); - function("predict_data", &exageostat::adapters::R_ExaGeoStatPredictData, List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, _["y"] = R_NilValue, _["z"] = R_NilValue)); + List::create(_["n"], _["kernel"], _["tile_size"], _["p_q"] = IntegerVector::create(1, 1), + _["time_slot"] = 1, _["computation"] = "exact", _["precision"] = "double", + _["cores_gpus"] = IntegerVector::create(1, 0), _["band"] = 1, _["max_rank"] = 500, + _["iTheta"], _["lb_ub"], _["eTheta"] = IntegerVector::create(-1, -1, -1), + _["verbose"] = "standard", _["dimension"] = "2D", _["mle_itr"] = 0, _["tol"] = 4, + _["prediction"] = IntegerVector::create(0, 0, 0, 0, 0), + _["paths"] = StringVector::create("", "", "", ""), _["save_data"] = 0)); + + function("simulate_data", &exageostat::adapters::R_ExaGeoStatLoadData, + List::create(_["hardware"], _["config"], _["data"])); + function("model_data", &exageostat::adapters::R_ExaGeoStatModelData, + List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, + _["y"] = R_NilValue, _["z"] = R_NilValue)); + function("predict_data", &exageostat::adapters::R_ExaGeoStatPredictData, + List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, + _["y"] = R_NilValue, _["z"] = R_NilValue)); } diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index 79c20980..c78d6662 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -12,6 +12,10 @@ * @date 2024-02-04 **/ +#ifdef USE_MPI +#include +#endif + #include #include #include @@ -74,11 +78,19 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV, const this->mpArgV = apArgV; mHeapAllocated = aEnableR; + int rank = 0; +#ifdef USE_MPI + MPI_Init(&this->mArgC,&this->mpArgV); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); +#endif + // Get the example name string example_name = apArgV[0]; // Remove the './' example_name.erase(0, 2); - LOGGER("Running " + example_name) + if(!rank){ + LOGGER("Running " + example_name) + } string argument; string argument_name; string argument_value; @@ -215,7 +227,7 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV, const //initlog InitLog(); } - this->PrintSummary(); + this->PrintSummary(rank); } @@ -637,58 +649,61 @@ void Configurations::InitTheta(vector &aTheta, const int &size) { } } -void Configurations::PrintSummary() { +void Configurations::PrintSummary(int aRank) { Verbose temp = this->GetVerbosity(); mVerbosity = STANDARD_MODE; - LOGGER("********************SUMMARY**********************") - if (this->GetIsSynthetic()) { - LOGGER("#Synthetic Dataset") - } else { - LOGGER("#Real Dataset") - } - LOGGER("#Number of Locations: " << this->GetProblemSize()) - LOGGER("#Threads per node: " << this->GetCoresNumber()) - LOGGER("#GPUs: " << this->GetGPUsNumbers()) - if (this->GetPrecision() == 1) { - LOGGER("#Precision: Double") - } else if (this->GetPrecision() == 0) { - LOGGER("#Precision: Single") - } else if (this->GetPrecision() == 2) { - LOGGER("#Precision: Single/Double") - } - LOGGER("#Dense Tile Size: " << this->GetDenseTileSize()) + if (!aRank) { + + LOGGER("********************SUMMARY**********************") + if (this->GetIsSynthetic()) { + LOGGER("#Synthetic Dataset") + } else { + LOGGER("#Real Dataset") + } + LOGGER("#Number of Locations: " << this->GetProblemSize()) + LOGGER("#Threads per node: " << this->GetCoresNumber()) + LOGGER("#GPUs: " << this->GetGPUsNumbers()) + if (this->GetPrecision() == 1) { + LOGGER("#Precision: Double") + } else if (this->GetPrecision() == 0) { + LOGGER("#Precision: Single") + } else if (this->GetPrecision() == 2) { + LOGGER("#Precision: Single/Double") + } + LOGGER("#Dense Tile Size: " << this->GetDenseTileSize()) #ifdef USE_HICMA - LOGGER("#Low Tile Size: " << this->GetLowTileSize()) + LOGGER("#Low Tile Size: " << this->GetLowTileSize()) #endif - if (this->GetComputation() == TILE_LOW_RANK) { - LOGGER("#Computation: Tile Low Rank") - } else if (this->GetComputation() == EXACT_DENSE) { - LOGGER("#Computation: Exact") - } else if (this->GetComputation() == DIAGONAL_APPROX) { - LOGGER("#Computation: Diagonal Approx") - } - - if (this->GetDimension() == Dimension2D) { - LOGGER("#Dimension: 2D") - } else if (this->GetDimension() == Dimension3D) { - LOGGER("#Dimension: 3D") - } else if (this->GetDimension() == DimensionST) { - LOGGER("#Dimension: ST") - } - LOGGER("#Kernel: " << this->GetKernelName()) - if (this->GetDistanceMetric() == EUCLIDEAN_DISTANCE) { - LOGGER("#Distance Metric: Euclidean distance") - } else { - LOGGER("#Distance Metric: Great Circle Distance") - } - LOGGER("#p: " << this->GetPGrid() << "\t\t #q: " << this->GetQGrid()) - if (this->GetIsOOC()) { - LOGGER("#Out Of Core (OOC) technology is enabled") + if (this->GetComputation() == TILE_LOW_RANK) { + LOGGER("#Computation: Tile Low Rank") + } else if (this->GetComputation() == EXACT_DENSE) { + LOGGER("#Computation: Exact") + } else if (this->GetComputation() == DIAGONAL_APPROX) { + LOGGER("#Computation: Diagonal Approx") + } + + if (this->GetDimension() == Dimension2D) { + LOGGER("#Dimension: 2D") + } else if (this->GetDimension() == Dimension3D) { + LOGGER("#Dimension: 3D") + } else if (this->GetDimension() == DimensionST) { + LOGGER("#Dimension: ST") + } + LOGGER("#Kernel: " << this->GetKernelName()) + if (this->GetDistanceMetric() == EUCLIDEAN_DISTANCE) { + LOGGER("#Distance Metric: Euclidean distance") + } else { + LOGGER("#Distance Metric: Great Circle Distance") + } + LOGGER("#p: " << this->GetPGrid() << "\t\t #q: " << this->GetQGrid()) + if (this->GetIsOOC()) { + LOGGER("#Out Of Core (OOC) technology is enabled") + } + LOGGER("*************************************************") + mVerbosity = temp; } - LOGGER("*************************************************") - mVerbosity = temp; } int Configurations::CalculateZObsNumber() { @@ -696,6 +711,10 @@ int Configurations::CalculateZObsNumber() { } Configurations::~Configurations() { +#ifdef USE_MPI + // Finalize the MPI environment. + MPI_Finalize(); +#endif if (mHeapAllocated) { for (size_t i = 0; i < this->mArgC; ++i) { delete[] this->mpArgV[i]; // Delete each string @@ -704,4 +723,3 @@ Configurations::~Configurations() { } this->mpArgV = nullptr; } - diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index dab41214..39708359 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -61,10 +61,10 @@ SyntheticGenerator::CreateData(Configurations &aConfigurations, CHAMELEON_Desc2Lap(ChamUpperLower, data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc, pMatrix, aConfigurations.GetProblemSize()); - if ( CHAMELEON_Comm_rank == 0 ){ + if (helpers::CommunicatorMPI::GetInstance()->GetRank() == 0){ helpers::DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), - aConfigurations.GetProblemSize(), aConfigurations.GetVariablesNumber(), path, + aConfigurations.GetProblemSize(), parameters_number, path, *data->GetLocations()); } delete[] pMatrix; diff --git a/src/data-loader/concrete/CSVLoader.cpp b/src/data-loader/concrete/CSVLoader.cpp index a8e07756..a255f642 100644 --- a/src/data-loader/concrete/CSVLoader.cpp +++ b/src/data-loader/concrete/CSVLoader.cpp @@ -138,6 +138,7 @@ void CSVLoader::ReadData(Configurations &aConfigurations, vector &aMeasure aConfigurations.SetProblemSize(index * aP / aConfigurations.GetTimeSlot()); file.close(); + LOGGER("Data is read from " << data_path << " successfully.") } template diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 7beb2bb2..4a679cf4 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -76,6 +76,8 @@ void ExaGeoStatHardware::FinalizeHardware(){ } ExaGeoStatHardware::~ExaGeoStatHardware() { + + exageostat::results::Results::GetInstance()->PrintEndSummary(); // finalize hardware using Chameleon if (mpChameleonContext) { CHAMELEON_Finalize() @@ -89,7 +91,6 @@ ExaGeoStatHardware::~ExaGeoStatHardware() { } #endif exageostat::helpers::CommunicatorMPI::GetInstance()->RemoveHardwareInitialization(); - exageostat::results::Results::GetInstance()->PrintEndSummary(); } void *ExaGeoStatHardware::GetHicmaContext() { diff --git a/src/helpers/CommunicatorMPI.cpp b/src/helpers/CommunicatorMPI.cpp index 9f8ac581..e527a8f9 100644 --- a/src/helpers/CommunicatorMPI.cpp +++ b/src/helpers/CommunicatorMPI.cpp @@ -23,29 +23,24 @@ CommunicatorMPI *CommunicatorMPI::GetInstance() { return mpInstance; } -bool CommunicatorMPI::GetRank() const { - +int CommunicatorMPI::GetRank() const{ #ifdef USE_MPI - if(!this->mIsHardwareInitialized){ - return false; + if(!mIsHardwareInitialized){ + return 0; } else{ - if(CHAMELEON_Comm_rank() == 0){ - return true; - } - return false; + return CHAMELEON_Comm_rank(); } #else - return true; + return 0; #endif } void CommunicatorMPI::SetHardwareInitialization() { - this->mIsHardwareInitialized = true; + mIsHardwareInitialized = true; } - -CommunicatorMPI *CommunicatorMPI::mpInstance = nullptr; - void CommunicatorMPI::RemoveHardwareInitialization() { - this->mIsHardwareInitialized = false; + mIsHardwareInitialized = false; } + +CommunicatorMPI *CommunicatorMPI::mpInstance = nullptr; diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index b375c547..ec9558f5 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -409,11 +409,11 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig T *pMatrix; VERBOSE("Writing generated data to the disk (Synthetic Dataset Generation Phase) .....") #ifdef CHAMELEON_USE_MPI - pMatrix = new T[n]; + pMatrix = new T[full_problem_size]; string path = aConfigurations.GetLoggerPath(); - ExaGeoStatDesc2Lap(pMatrix, n, CHAM_descZ, EXAGEOSTAT_UPPER_LOWER); + ExaGeoStatDesc2Lap(pMatrix, full_problem_size, CHAM_descZ, EXAGEOSTAT_UPPER_LOWER); if ( CHAMELEON_Comm_rank() == 0 ){ - helpers::DiskWriter::WriteVectorsToDisk(*pMatrix, n, P, path, *apLocation1); + helpers::DiskWriter::WriteVectorsToDisk(*pMatrix, full_problem_size, P, path, *apLocation1); } delete[] pMatrix; #else @@ -764,28 +764,23 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< *mspe = -1; } -#if defined(CHAMELEON_USE_MPI) - if(CHAMELEON_My_Mpi_Rank() == 0) - { -#endif - if (aConfiguration.GetLogger()) { - fprintf(aConfiguration.GetFileLogPath(), - "\n\n# of missing observations :%d\n\nPrediction Execution Time: %.8f, ""Flops: %.8f, Mean Square Prediction Error (MSPE): %.8f\n\n", - aZMissNumber, (mat_gen_time + mat_gen_time_2 + time_solve + time_mspe), (flops / 1e9 / (time_solve)), - *mspe); - } - VERBOSE("- Z Actual .. Z Miss") - for (i = 0; i < aZMissNumber; i++) { - VERBOSE(" (" << apZActual[i] << ", " << apZMiss[i] << ")") - } - - results::Results::GetInstance()->SetMSPEExecutionTime(time_solve + time_gemm + time_trsm); - results::Results::GetInstance()->SetMSPEFlops((flops / 1e9 / (time_solve + time_gemm))); - results::Results::GetInstance()->SetMSPEError(*mspe); + if(helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) { + if (aConfiguration.GetLogger()) { + fprintf(aConfiguration.GetFileLogPath(), + "\n\n# of missing observations :%d\n\nPrediction Execution Time: %.8f, ""Flops: %.8f, Mean Square Prediction Error (MSPE): %.8f\n\n", + aZMissNumber, (mat_gen_time + mat_gen_time_2 + time_solve + time_mspe), + (flops / 1e9 / (time_solve)), + *mspe); + } + VERBOSE("- Z Actual .. Z Miss") + for (i = 0; i < aZMissNumber; i++) { + VERBOSE(" (" << apZActual[i] << ", " << apZMiss[i] << ")") + } -#if defined(CHAMELEON_USE_MPI) + results::Results::GetInstance()->SetMSPEExecutionTime(time_solve + time_gemm + time_trsm); + results::Results::GetInstance()->SetMSPEFlops((flops / 1e9 / (time_solve + time_gemm))); + results::Results::GetInstance()->SetMSPEError(*mspe); } -#endif T *all_mspe = new T[3]; all_mspe[0] = *mspe; diff --git a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp index b3c70554..9da5263e 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp @@ -368,7 +368,7 @@ T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardw VERBOSE("Done.") //Distribute the values in the case of MPI -#if defined(CHAMELEON_USE_MPI) +#ifdef USE_MPI MPI_Bcast(&loglik, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD ); #endif diff --git a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp index 8b087393..f26a4912 100644 --- a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp @@ -12,6 +12,10 @@ * @date 2024-02-04 **/ +#ifdef USE_MPI +#include +#endif + #include using namespace std; @@ -99,7 +103,7 @@ T HicmaImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const Kernel &aKernel) { - this->SetContext(aHardware.GetContext(aConfigurations.GetComputation())); + this->SetContext(ExaGeoStatHardware::GetContext(aConfigurations.GetComputation())); if (!aData->GetDescriptorData()->GetIsDescriptorInitiated()) { this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(),aKernel.GetVariablesNumber(), apMeasurementsMatrix); } @@ -247,7 +251,8 @@ T HicmaImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, VERBOSE(" Done.") double global_sum; -#if defined(CHAMELEON_USE_MPI) + double local_sum = *product; +#ifdef USE_MPI MPI_Allreduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); #else global_sum = *product; diff --git a/tests/R-tests/TestExaGeoStat.R b/tests/R-tests/TestExaGeoStat.R index 25042bff..a41f4623 100644 --- a/tests/R-tests/TestExaGeoStat.R +++ b/tests/R-tests/TestExaGeoStat.R @@ -29,12 +29,43 @@ exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_sou model_data(hardware=hardware, config=config, data=exageostat_data) predict_data(hardware=hardware, config=config, data=exageostat_data) -paste("---------------------------------------------------------------") -hardware$finalize_hardware() +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with Data Generation - saving data with default path") + +save_data <- TRUE +# if you didn't change the log_path it will be saved in the default path of project_path/synthetic_ds +log_path <- "" +# data path is where to read data from +data_path <- "" +# observations file path is where to read observation file +observations_file <- "" +# recovery file path is where to read recovery file +recovery_file <- "" +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data) +data_source <- new(Data, problem_size, "2D") +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) + + +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with Data Generation - Reading Data") + +save_data <- FALSE +log_path <- "" +# data path is where to read data from +data_path <- "./synthetic_ds/SYN_16_1" +# observations file path is where to read observation file +observations_file <- "" +# recovery file path is where to read recovery file +recovery_file <- "" + +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data) +data_source <- new(Data, problem_size, "2D") +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) + + +paste("---------------------------------------------------------------------------------------------") paste("ExaGeoStat with data Modeling only") -ncores <- 5 -hardware <- new(Hardware, computation, ncores, ngpus) config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=10) exageostat_data <- new(Data, problem_size, "2D") numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, @@ -63,19 +94,6 @@ locations_y = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) model_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) paste("---------------------------------------------------------------") -paste("ExaGeoStat with data Prediction only - using mspe only") - -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,1,0,0,0)) -predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) - -paste("---------------------------------------------------------------") -paste("ExaGeoStat with data Prediction only - using idw only") - -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,1,0,0)) -predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) - -paste("---------------------------------------------------------------") -paste("ExaGeoStat with data Prediction only - using MLOE_MMOM only") - -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,0,0,1)) +paste("ExaGeoStat with data Prediction only - all prediction functions") +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,1,1,1,1)) predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) diff --git a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp index fead63b2..6a0a5182 100644 --- a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp +++ b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp @@ -24,9 +24,12 @@ using namespace std; using namespace exageostat::adapters; void TEST_ALL_R_METHODS() { - SECTION("R METHODS"){ + SECTION("R METHODS") { - auto configurations = R_InitializeArguments(16, "univariate_matern_stationary", {8, 0}, {1, 1}, 1, "exact", "double", {1, 0}, 0, 1, {1,0.1,0.5}, { {0.1,0.1,0.1}, {5,5,5}}, {-1,-1,-1}, "standard", "2D", 10, 4, {5,1,1,1,1}); + auto configurations = R_InitializeArguments(16, "univariate_matern_stationary", {8, 0}, {1, 1}, 1, "exact", + "double", {1, 0}, 0, 1, {1, 0.1, 0.5}, {{0.1, 0.1, 0.1}, {5, 5, 5}}, + {-1, -1, -1}, "standard", "2D", 10, 4, {5, 1, 1, 1, 1}, + {"", "", "", ""}, false); auto hardware = ExaGeoStatHardware("exact", 1, 0); auto data_source = new ExaGeoStatData(16, "2D"); auto exageostat_data = R_ExaGeoStatLoadData(&hardware, configurations, data_source); diff --git a/tests/heavy-tests/ExamplesTests.cpp b/tests/heavy-tests/ExamplesTests.cpp index 4b1906e9..7566ca7a 100644 --- a/tests/heavy-tests/ExamplesTests.cpp +++ b/tests/heavy-tests/ExamplesTests.cpp @@ -28,7 +28,7 @@ TEST_CASE("EXAMPLES") { path currentPath = current_path().parent_path().parent_path() / "examples"; SECTION("Configuration"){ - path configurations_example = currentPath / "configurations/Example_Configurations "; + path configurations_example = currentPath / "configurations/Example_Configurations_Setup "; string arguments_string = "--N=10 --dts=8 --kernel=univariate_matern_stationary"; cout << "Running Configurations example with arguments: " + arguments_string << endl << flush; From ae0d55c6cf3a7bb8d7757cb26f6b1448a4f1eedc Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 26 Feb 2024 14:09:51 +0200 Subject: [PATCH 30/82] fix: typpo in Initialize --- src/hardware/ExaGeoStatHardware.cpp | 2 +- src/linear-algebra-solvers/LinearAlgebraMethods.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 4a679cf4..7b17a727 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -35,7 +35,7 @@ ExaGeoStatHardware::ExaGeoStatHardware(const std::string &aComputation, const in void ExaGeoStatHardware::InitHardware(const Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber) { - LOGGER("** Initialise ExaGeoStat hardware **") + LOGGER("** Initialize ExaGeoStat hardware **") int tag_width = 31, tag_sep = 26; // Init hardware using Chameleon diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index ec9558f5..c45542a4 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -1474,7 +1474,7 @@ template int LinearAlgebraMethods::ExaGeoStatMLEMSPETileAsync(void *apDescZPredict, void *apDescZMiss, void *apDescError, void *apSequence, void *apRequest) { - // Check for Initialise the Chameleon context. + // Check for Initialize the Chameleon context. if (!this->mpContext) { throw std::runtime_error( "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); @@ -1513,7 +1513,7 @@ int LinearAlgebraMethods::ExaGeoStatMLETileAsyncMLOEMMOM(void *apDescExpr2, v void *apDescMLOE, void *apDescMMOM, void *apSequence, void *apRequest) { - // Check for Initialise the Chameleon context. + // Check for Initialize the Chameleon context. if (!this->mpContext) { throw std::runtime_error( "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); From 0110a82533421736dab235a3c10fe49385624463 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 26 Feb 2024 15:53:15 +0200 Subject: [PATCH 31/82] fix passing numbers of theta --- src/Rcpp-adapters/FunctionsAdapter.cpp | 61 +++++++++++++++++++++----- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/Rcpp-adapters/FunctionsAdapter.cpp b/src/Rcpp-adapters/FunctionsAdapter.cpp index f0c52feb..13f04938 100644 --- a/src/Rcpp-adapters/FunctionsAdapter.cpp +++ b/src/Rcpp-adapters/FunctionsAdapter.cpp @@ -52,16 +52,57 @@ namespace exageostat::adapters { argStrings.push_back("--gpus=" + to_string(aCoresGPUsNumber[1])); argStrings.push_back("--band=" + to_string(aBand)); argStrings.push_back("--max_rank=" + to_string(aMaxRank)); - argStrings.push_back("--iTheta=" + to_string(aInitialTheta[0]) + ":" + to_string(aInitialTheta[1]) + ":" + - to_string(aInitialTheta[2])); - argStrings.push_back( - "--lb=" + to_string(aLowerUpperBounds[0][0]) + ":" + to_string(aLowerUpperBounds[0][1]) + ":" + - to_string(aLowerUpperBounds[0][2])); - argStrings.push_back( - "--ub=" + to_string(aLowerUpperBounds[1][0]) + ":" + to_string(aLowerUpperBounds[1][1]) + ":" + - to_string(aLowerUpperBounds[1][2])); - argStrings.push_back("--eTheta=" + to_string(aEstimatedTheta[0]) + ":" + to_string(aEstimatedTheta[1]) + ":" + - to_string(aEstimatedTheta[2])); + + string initial_theta = "--iTheta="; + for(int i = 0; i < aInitialTheta.size(); i++){ + if (i != aInitialTheta.size() - 1){ + initial_theta += to_string(aInitialTheta[i]) + ":"; + } else{ + initial_theta += to_string(aInitialTheta[i]); + } + } + argStrings.push_back(initial_theta); + string lower_bound = "--lb="; + string upper_bound = "--ub="; + for(int i = 0; i < aLowerUpperBounds[0].size(); i++){ + if (i != aLowerUpperBounds[0].size() - 1) { + lower_bound += to_string(aLowerUpperBounds[0][i]) + ":"; + } + else{ + lower_bound += to_string(aLowerUpperBounds[0][i]); + } + } + for(int i = 0; i < aLowerUpperBounds[1].size(); i++){ + if (i != aLowerUpperBounds[1].size() - 1) { + upper_bound += to_string(aLowerUpperBounds[1][i]) + ":"; + } + else{ + upper_bound += to_string(aLowerUpperBounds[1][i]); + } + } + argStrings.push_back(lower_bound); + argStrings.push_back(upper_bound); + + string estimated_theta = "--eTheta="; + for(int i = 0; i < aInitialTheta.size(); i++){ + if (i != aInitialTheta.size() - 1) { + if(i < aEstimatedTheta.size()){ + estimated_theta += to_string(aEstimatedTheta[i]) + ":"; + } + else{ + estimated_theta += "-1:"; + } + } + else{ + if(i < aEstimatedTheta.size()) { + estimated_theta += to_string(aEstimatedTheta[i]); + } + else{ + estimated_theta += "-1"; + } + } + } + argStrings.push_back(estimated_theta); argStrings.push_back("--verbose=" + aVerbose); argStrings.push_back("--dimension=" + aDimension); argStrings.push_back("--max_mle_iterations=" + to_string(aMaxMleIterations)); From 9a4081fed639875e0f5bf61d166e58ddf72c974d Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 27 Feb 2024 16:37:12 +0200 Subject: [PATCH 32/82] fix: performance of gsl --- cmake/macros/ImportDependency.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/macros/ImportDependency.cmake b/cmake/macros/ImportDependency.cmake index 5306bf4a..b32580dd 100644 --- a/cmake/macros/ImportDependency.cmake +++ b/cmake/macros/ImportDependency.cmake @@ -65,7 +65,9 @@ macro(ImportDependency name tag version url flag components is_cmake is_git auto link_directories(${${name}_LIBRARY_DIRS}) include_directories(${${name}_INCLUDE_DIRS}) include_directories(AFTER ${${name}_INCLUDE_DIRS_DEP}) - list(APPEND LIBS ${${name}_LIBRARIES}) + if(NOT ${name} STREQUAL "GSL") + list(APPEND LIBS ${${name}_LIBRARIES}) + endif() list(APPEND LIBS ${${name}_LIBRARIES_DEP}) endmacro() \ No newline at end of file From 2f65d19bc680603f29f25353df7e1c9bc214d9fc Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 27 Feb 2024 17:18:49 +0200 Subject: [PATCH 33/82] fix: GPU support --- inst/include/utilities/ErrorHandler.hpp | 2 +- tests/R-tests/TestExaGeoStat.R | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/inst/include/utilities/ErrorHandler.hpp b/inst/include/utilities/ErrorHandler.hpp index 5d225f0a..4cb4214b 100644 --- a/inst/include/utilities/ErrorHandler.hpp +++ b/inst/include/utilities/ErrorHandler.hpp @@ -85,7 +85,7 @@ class APIException : public std::exception{ { #ifdef USING_R std::string s="GPU Assert: "+std::string(cudaGetErrorString(aCode)); - Rcpp::stop(s); +// Rcpp::stop(s); #else char s[200]; sprintf((char*)s,"GPU Assert: %s %s %d\n", cudaGetErrorString(aCode), aFile, aLine); diff --git a/tests/R-tests/TestExaGeoStat.R b/tests/R-tests/TestExaGeoStat.R index a41f4623..f0cce4a7 100644 --- a/tests/R-tests/TestExaGeoStat.R +++ b/tests/R-tests/TestExaGeoStat.R @@ -22,7 +22,7 @@ lts <- 0 computation <- "exact" hardware <- new(Hardware, computation, ncores, ngpus) -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1)) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1)) data_source <- new(Data, problem_size, "2D") exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) @@ -41,7 +41,7 @@ data_path <- "" observations_file <- "" # recovery file path is where to read recovery file recovery_file <- "" -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data) data_source <- new(Data, problem_size, "2D") exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) @@ -58,7 +58,7 @@ observations_file <- "" # recovery file path is where to read recovery file recovery_file <- "" -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data) data_source <- new(Data, problem_size, "2D") exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) @@ -66,7 +66,7 @@ exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_sou paste("---------------------------------------------------------------------------------------------") paste("ExaGeoStat with data Modeling only") -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=10) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=10) exageostat_data <- new(Data, problem_size, "2D") numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, @@ -95,5 +95,5 @@ model_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_valu paste("---------------------------------------------------------------") paste("ExaGeoStat with data Prediction only - all prediction functions") -config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,1,1,1,1)) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,1,1,1,1)) predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) From d72800a75f039d66d2827d4a9cbaa3523868bae9 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 3 Mar 2024 12:49:05 +0200 Subject: [PATCH 34/82] fix:mspe --- examples/end-to-end/CMakeLists.txt | 2 + .../DataGenerationAndPrediction.cpp | 44 +++++++++++++++++++ .../LinearAlgebraMethods.cpp | 4 ++ 3 files changed, 50 insertions(+) create mode 100644 examples/end-to-end/DataGenerationAndPrediction.cpp diff --git a/examples/end-to-end/CMakeLists.txt b/examples/end-to-end/CMakeLists.txt index 9ed3a380..e4020a01 100644 --- a/examples/end-to-end/CMakeLists.txt +++ b/examples/end-to-end/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(Example_Data_Modeling ${CMAKE_CURRENT_SOURCE_DIR}/DataModeling.cp add_executable(Example_Data_Generation_and_Modeling ${CMAKE_CURRENT_SOURCE_DIR}/DataGenerationAndModeling.cpp) add_executable(Example_Data_Generation_Modeling_and_Prediction ${CMAKE_CURRENT_SOURCE_DIR}/DataGenerationModelingAndPrediction.cpp) add_executable(Example_Data_Prediction ${CMAKE_CURRENT_SOURCE_DIR}/DataPrediction.cpp) +add_executable(Example_Data_Generation_and_Prediction ${CMAKE_CURRENT_SOURCE_DIR}/DataGenerationAndPrediction.cpp) # Link the target executable with the project and any additional libraries target_link_libraries(Example_Data_Generation PUBLIC ${PROJECT_NAME}_INTERFACE) @@ -21,3 +22,4 @@ target_link_libraries(Example_Data_Modeling PUBLIC ${PROJECT_NAME}_INTERFACE) target_link_libraries(Example_Data_Generation_and_Modeling PUBLIC ${PROJECT_NAME}_INTERFACE) target_link_libraries(Example_Data_Generation_Modeling_and_Prediction PUBLIC ${PROJECT_NAME}_INTERFACE) target_link_libraries(Example_Data_Prediction PUBLIC ${PROJECT_NAME}_INTERFACE) +target_link_libraries(Example_Data_Generation_and_Prediction PUBLIC ${PROJECT_NAME}_INTERFACE) diff --git a/examples/end-to-end/DataGenerationAndPrediction.cpp b/examples/end-to-end/DataGenerationAndPrediction.cpp new file mode 100644 index 00000000..aace2c7a --- /dev/null +++ b/examples/end-to-end/DataGenerationAndPrediction.cpp @@ -0,0 +1,44 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file DataGenerationAndPrediction.cpp + * @brief This program This program either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data, then predicts missing measurements using the ExaGeoStat library. + * @details The program takes command line arguments to configure the data generation. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-03-03 +**/ + +#include +#include + +using namespace exageostat::api; +using namespace exageostat::dataunits; + +/** + * @brief Main entry point for the Data Generation & Data Modeling program. + * @details This function either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data, models it, and predicts missing values. + * @param[in] argc The number of command line arguments. + * @param[in] argv An array of command line argument strings. + * @return An integer indicating the success or failure of the program. + */ +int main(int argc, char **argv) { + + // Create a new configurations object. + Configurations configurations; + // Initialize the arguments with the provided command line arguments + configurations.InitializeArguments(argc, argv); + // Initialize the ExaGeoStat Hardware + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), + configurations.GetGPUsNumbers()); + // Load data by either read from file or create synthetic data. + std::unique_ptr> data; + ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); + // Prediction module + ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data); + + return 0; +} diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index c45542a4..d707fbb9 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -1335,6 +1335,10 @@ LinearAlgebraMethods::ExaGeoStatGetZObs(Configurations &aConfigurations, T *a dts, dts * dts, full_problem_size, 1, 0, 0, full_problem_size, 1, p_grid, q_grid); z_desc = (CHAM_desc_t *) aDescData.GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_COPY).chameleon_desc; } + double epsilon = 1e-8; + if (abs(((T *)z_desc->mat)[0]) < epsilon) { + z_desc = (CHAM_desc_t *) aDescData.GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc; + } this->ExaGeoStatDesc2Lap(apZ, aSize, z_desc, UpperLower::EXAGEOSTAT_UPPER_LOWER); } From 96931cf2bd3b3d462b9ddf14d327b632fe17e24b Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 3 Mar 2024 15:20:02 +0200 Subject: [PATCH 35/82] updated devel --- CMakeLists.txt | 5 - cmake/macros/ImportDependency.cmake | 4 +- examples/CMakeLists.txt | 9 +- examples/data-loader/CMakeLists.txt | 15 +++ examples/data-loader/CSVLoader.cpp | 71 ++++++++++ examples/descriptors/CMakeLists.txt | 23 ++++ examples/descriptors/ChameleonDescriptor.cpp | 95 ++++++++++++++ .../descriptors/ChameleonToHicmaConverter.cpp | 122 ++++++++++++++++++ examples/descriptors/HicmaDescriptor.cpp | 100 ++++++++++++++ examples/hardware/CMakeLists.txt | 16 +++ examples/hardware/ExaGeoStatHardware.cpp | 31 +++++ inst/include/common/Definitions.hpp | 12 +- src/runtime/starpu/CMakeLists.txt | 6 + tests/heavy-tests/ExamplesTests.cpp | 1 - 14 files changed, 498 insertions(+), 12 deletions(-) create mode 100644 examples/data-loader/CMakeLists.txt create mode 100644 examples/data-loader/CSVLoader.cpp create mode 100644 examples/descriptors/CMakeLists.txt create mode 100644 examples/descriptors/ChameleonDescriptor.cpp create mode 100644 examples/descriptors/ChameleonToHicmaConverter.cpp create mode 100644 examples/descriptors/HicmaDescriptor.cpp create mode 100644 examples/hardware/CMakeLists.txt create mode 100644 examples/hardware/ExaGeoStatHardware.cpp create mode 100644 src/runtime/starpu/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index b34b08c9..5fca23fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,11 +55,6 @@ message(STATUS "CMAKE VERSION: ${CMAKE_VERSION}") enable_language(CXX) # Get the current path of the project. add_compile_definitions(PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}/") -# Add Paths definitions -add_definitions( - -DLOG_PATH="${PROJECT_SOURCE_DIR}/synthetic_ds/" - -DKERNELS_PATH="${PROJECT_SOURCE_DIR}/inst/include/kernels/concrete/" -) diff --git a/cmake/macros/ImportDependency.cmake b/cmake/macros/ImportDependency.cmake index c2de92f8..470e22d7 100644 --- a/cmake/macros/ImportDependency.cmake +++ b/cmake/macros/ImportDependency.cmake @@ -64,7 +64,9 @@ macro(ImportDependency name tag version url flag components is_cmake is_git auto link_directories(${${name}_LIBRARY_DIRS}) include_directories(${${name}_INCLUDE_DIRS}) include_directories(AFTER ${${name}_INCLUDE_DIRS_DEP}) - list(APPEND LIBS ${${name}_LIBRARIES}) + if(NOT ${name} STREQUAL "GSL") + list(APPEND LIBS ${${name}_LIBRARIES}) + endif() list(APPEND LIBS ${${name}_LIBRARIES_DEP}) endmacro() \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index fd6d1ce3..6bd804a5 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,11 +5,14 @@ # @file CMakeLists.txt # @brief Includes subdirectories for different modules of the ExaGeoStat software package. -# @version 1.0.0 +# @version 1.0.1 # @author Mahmoud ElKarargy -# @date 2023-01-31 +# @date 2024-02-24 # Include subdirectories for end-to-end module, configurations module and data-generators module. -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/end-to-end) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/configurations) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-generators) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-loader) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/descriptors) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/end-to-end) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/hardware) diff --git a/examples/data-loader/CMakeLists.txt b/examples/data-loader/CMakeLists.txt new file mode 100644 index 00000000..dc85167c --- /dev/null +++ b/examples/data-loader/CMakeLists.txt @@ -0,0 +1,15 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2024-02-14 + +# Define the target executable +add_executable(Example_CSV_Loader ${CMAKE_CURRENT_SOURCE_DIR}/CSVLoader.cpp) + +# Link the target executable with the project and any additional libraries +target_link_libraries(Example_CSV_Loader PRIVATE ${PROJECT_NAME}_INTERFACE) diff --git a/examples/data-loader/CSVLoader.cpp b/examples/data-loader/CSVLoader.cpp new file mode 100644 index 00000000..883221f0 --- /dev/null +++ b/examples/data-loader/CSVLoader.cpp @@ -0,0 +1,71 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file CSVLoader.cpp + * @brief Example of the CSVLoader class + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-02-18 +**/ + +#include +#include + +using namespace std; + +using namespace exageostat::kernels; +using namespace exageostat::hardware; +using namespace exageostat::common; +using namespace exageostat::generators; +using namespace exageostat::configurations; +using namespace exageostat::dataLoader::csv; + +int main(int argc, char **argv) { + LOGGER("** Example of CSV Loader **") + + // Create and Initialize a new configurations object. + Configurations configurations; + configurations.InitializeArguments(argc, argv); + + // Generate Data and Log it into file + configurations.SetLogger(true); + configurations.InitializeDataGenerationArguments(); + + // Initialize ExaGeoStat Hardware and Kernel. + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), + configurations.GetGPUsNumbers()); + + Kernel *kernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), configurations.GetTimeSlot()); + int kernel_variables = kernel->GetVariablesNumber(); + + // Create a unique pointer to a DataGenerator object and generate data + configurations.SetIsSynthetic(true); + configurations.SetIsCSV(false); + unique_ptr> synthetic_generator = DataGenerator::CreateGenerator(configurations); + auto data = synthetic_generator->CreateData(configurations, hardware, *kernel); + + // Read csv file using data loader + vector measurements_vector; + vector x_locations; + vector y_locations; + vector z_locations; + + auto loader = CSVLoader::GetInstance(); + loader->ReadData(configurations, measurements_vector, x_locations, y_locations, z_locations, kernel_variables); + + // Print loaded data + LOGGER("Data Loaded:") + for (int i=0;i +#include +#include +#include +#include + +using namespace std; + +using namespace exageostat::common; +using namespace exageostat::kernels; +using namespace exageostat::plugins; +using namespace exageostat::hardware; +using namespace exageostat::dataunits; +using namespace exageostat::configurations; + +int main(int argc, char **argv) { + + LOGGER("** Example of Chameleon Descriptor **") + + // Initialize Configuration + Configurations configuration; + configuration.InitializeArguments(argc, argv); + + // Initialize Hardware and Data + auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), + configuration.GetGPUsNumbers()); + unique_ptr> data = make_unique>(); + Kernel *kernel = PluginRegistry>::Create( + configuration.GetKernelName(), configuration.GetTimeSlot()); + + // Get arguments for Descriptors Initialization + int kernel_variables_number = kernel->GetVariablesNumber(); + int config_problem_size = configuration.GetProblemSize(); + int config_full_problem_size = config_problem_size * kernel_variables_number; + int config_dts = configuration.GetDenseTileSize(); + int config_p_grid = configuration.GetPGrid(); + int config_q_grid = configuration.GetQGrid(); + bool config_is_OOC = configuration.GetIsOOC(); + + // Randomly Initialized Matrix of Data + std::vector matrix(config_problem_size * config_problem_size); + for (int i = 0; i < config_problem_size; ++i) { + matrix[i] = i; + } + + // Set Data Descriptor + data->SetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C, config_is_OOC, matrix.data(), EXAGEOSTAT_REAL_DOUBLE, + config_dts, + config_dts, + config_dts * config_dts, config_full_problem_size, config_full_problem_size, 0, 0, + config_full_problem_size, + config_full_problem_size, config_p_grid, config_q_grid); + + auto *CHAM_descriptorC = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; + + //Print Descriptor Parameters + LOGGER("** Descriptor Parameters:") + LOGGER(" Problem Size: " << CHAM_descriptorC->m) + LOGGER(" Dense Tile Size: " << CHAM_descriptorC->mb) + + LOGGER(" Entire Number of Rows :" << CHAM_descriptorC->lm) + LOGGER(" Entire Number of Columns :" << CHAM_descriptorC->ln) + + LOGGER(" Number of Sub-matrix Tile Rows: " << CHAM_descriptorC->mt) + LOGGER(" Number of Sub-matrix Tile Columns: " << CHAM_descriptorC->nt) + + LOGGER(" Number of Rows of 2D distribution grid: " << CHAM_descriptorC->p) + LOGGER(" Number of Rows of 2D distribution grid: " << CHAM_descriptorC->q) + + LOGGER(" Is Matrix Not Fit in Memory: " << CHAM_descriptorC->ooc) + LOGGER(" Size including Padding: " << CHAM_descriptorC->bsiz) + + // Print Data Matrix of Descriptor + LOGGER("** Data in Matrix:") + LOGGER_2("",0) + auto *data_mat = (double *) CHAM_descriptorC->mat; + for (int i = 0; i < config_problem_size; ++i) { + LOGGER_PRECISION_1(" " << data_mat[i],0) + } + + delete kernel; +} diff --git a/examples/descriptors/ChameleonToHicmaConverter.cpp b/examples/descriptors/ChameleonToHicmaConverter.cpp new file mode 100644 index 00000000..3cc7375d --- /dev/null +++ b/examples/descriptors/ChameleonToHicmaConverter.cpp @@ -0,0 +1,122 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ChameleonDescriptor.cpp + * @brief Example file for the Chameleon to Hicma Converter. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-02-14 +**/ + +#include +#include +#include +#include +#include + +using namespace std; + +using namespace exageostat::common; +using namespace exageostat::kernels; +using namespace exageostat::plugins; +using namespace exageostat::hardware; +using namespace exageostat::dataunits; +using namespace exageostat::configurations; + +int main(int argc, char **argv) { + + LOGGER("** Example of Chameleon To Hicma Converter **") + + // Initialize Configuration + Configurations configuration; + configuration.InitializeArguments(argc, argv); + + // Initialize Hardware and Data + auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), + configuration.GetGPUsNumbers()); + unique_ptr> data = make_unique>(); + Kernel *kernel = PluginRegistry>::Create( + configuration.GetKernelName(), configuration.GetTimeSlot()); + + // Get arguments for Descriptors Initialization + int kernel_variables_number = kernel->GetVariablesNumber(); + int config_problem_size = configuration.GetProblemSize(); + int config_full_problem_size = config_problem_size * kernel_variables_number; + int config_dts = configuration.GetDenseTileSize(); + int config_p_grid = configuration.GetPGrid(); + int config_q_grid = configuration.GetQGrid(); + bool config_is_OOC = configuration.GetIsOOC(); + + // Randomly Initialized Matrix of Data + std::vector matrix(config_problem_size); + for (int i = 0; i < config_problem_size; ++i) { + matrix[i] = i; + } + + // Set Data Descriptor + data->SetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C, config_is_OOC, matrix.data(), EXAGEOSTAT_REAL_DOUBLE, + config_dts, + config_dts, + config_dts * config_dts, config_full_problem_size, config_full_problem_size, 0, 0, + config_full_problem_size, + config_full_problem_size, config_p_grid, config_q_grid); + + auto *CHAM_descriptorC = data->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C).chameleon_desc; + + //Print Descriptor Attributes Before and After conversion + LOGGER("** Chameleon Descriptor, Before Conversion:") + LOGGER(" Problem Size: " << CHAM_descriptorC->m) + LOGGER(" Dense Tile Size: " << CHAM_descriptorC->mb) + + LOGGER(" Entire Number of Rows :" << CHAM_descriptorC->lm) + LOGGER(" Entire Number of Columns :" << CHAM_descriptorC->ln) + + LOGGER(" Number of Sub-matrix Tile Rows: " << CHAM_descriptorC->mt) + LOGGER(" Number of Sub-matrix Tile Columns: " << CHAM_descriptorC->nt) + + LOGGER(" Number of Rows of 2D distribution grid: " << CHAM_descriptorC->p) + LOGGER(" Number of Rows of 2D distribution grid: " << CHAM_descriptorC->q) + + LOGGER(" Is Matrix Not Fit in Memory: " << CHAM_descriptorC->ooc) + LOGGER(" Size including Padding: " << CHAM_descriptorC->bsiz) + + // Print Data Matrix of Descriptor + LOGGER("** Data in Matrix of Chameleon descriptor:") + LOGGER_2("",0) + auto *cham_mat = (double *) CHAM_descriptorC->mat; + for (int i = 0; i < config_problem_size; ++i) { + LOGGER_PRECISION_1(" " << cham_mat[i],0) + } + + auto *HICMA_descriptorC = data->ConvertChameleonToHicma(CHAM_descriptorC); + + LOGGER("\n\t\t ** Hicma Descriptor, After Conversion:") + LOGGER(" Problem Size: " << HICMA_descriptorC->m) + LOGGER(" Dense Tile Size: " << HICMA_descriptorC->mb) + + LOGGER(" Entire Number of Rows :" << HICMA_descriptorC->lm) + LOGGER(" Entire Number of Columns :" << HICMA_descriptorC->ln) + + LOGGER(" Number of Sub-matrix Tile Rows: " << HICMA_descriptorC->mt) + LOGGER(" Number of Sub-matrix Tile Columns: " << HICMA_descriptorC->nt) + + LOGGER(" Number of Rows of 2D distribution grid: " << HICMA_descriptorC->p) + LOGGER(" Number of Rows of 2D distribution grid: " << HICMA_descriptorC->q) + + LOGGER(" Is Matrix Not Fit in Memory: " << HICMA_descriptorC->ooc) + LOGGER(" Size including Padding: " << HICMA_descriptorC->bsiz) + + // Print Data Matrix of Descriptor + LOGGER("** Data in Matrix of Hicma descriptor:") + auto *hicma_mat = (double *) HICMA_descriptorC->mat; + LOGGER_2("",0) + for (int i = 0; i < config_problem_size; ++i) { + LOGGER_PRECISION_1(" " << hicma_mat[i],0) + } + + delete kernel; + delete HICMA_descriptorC; +} diff --git a/examples/descriptors/HicmaDescriptor.cpp b/examples/descriptors/HicmaDescriptor.cpp new file mode 100644 index 00000000..5bea39c1 --- /dev/null +++ b/examples/descriptors/HicmaDescriptor.cpp @@ -0,0 +1,100 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file HicmaDescriptor.cpp + * @brief Example file for the Hicma descriptor. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-02-14 +**/ + +#include +#include +#include +#include +#include + +using namespace std; + +using namespace exageostat::common; +using namespace exageostat::kernels; +using namespace exageostat::plugins; +using namespace exageostat::hardware; +using namespace exageostat::dataunits; +using namespace exageostat::configurations; + +int main(int argc, char **argv) { + + LOGGER("** Example of Hicma Descriptor **") + + // Initialize Synthetic Configuration + Configurations configuration; + configuration.InitializeArguments(argc, argv); + + //Check for TLR computation specific for HICMA descriptor + if (configuration.GetComputation() != TILE_LOW_RANK) { + LOGGER("You must provide TILE_LOW_RANK computation to initialize HICMA descriptor.") + LOGGER("Consider adding \"--computation=tlr\" to the arguments") + return 0; + } + // Initialize Hardware and Data + auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), + configuration.GetGPUsNumbers()); + unique_ptr> data = make_unique>(); + Kernel *kernel = PluginRegistry>::Create( + configuration.GetKernelName(), configuration.GetTimeSlot()); + + // Get arguments for Descriptors Initialization + int kernel_variables_number = kernel->GetVariablesNumber(); + int config_problem_size = configuration.GetProblemSize(); + int config_full_problem_size = config_problem_size * kernel_variables_number; + int config_dts = configuration.GetDenseTileSize(); + int config_p_grid = configuration.GetPGrid(); + int config_q_grid = configuration.GetQGrid(); + bool config_is_OOC = configuration.GetIsOOC(); + + // Randomly Initialized Matrix of Data + std::vector matrix(config_problem_size * config_problem_size); + for (int i = 0; i < config_problem_size; ++i) { + matrix[i] = i; + } + + // Set Data Descriptor + data->SetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_C, config_is_OOC, matrix.data(), EXAGEOSTAT_REAL_DOUBLE, + config_dts, + config_dts, + config_dts * config_dts, config_full_problem_size, config_full_problem_size, 0, 0, + config_full_problem_size, + config_full_problem_size, config_p_grid, config_q_grid); + + auto *HICMA_descriptorC = data->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_C).hicma_desc; + + //Print Descriptor Attributes + LOGGER(" Problem Size: " << HICMA_descriptorC->m) + LOGGER(" Dense Tile Size: " << HICMA_descriptorC->mb) + + LOGGER(" Entire Number of Rows :" << HICMA_descriptorC->lm) + LOGGER(" Entire Number of Columns :" << HICMA_descriptorC->ln) + + LOGGER(" Number of Sub-matrix Tile Rows: " << HICMA_descriptorC->mt) + LOGGER(" Number of Sub-matrix Tile Columns: " << HICMA_descriptorC->nt) + + LOGGER(" Number of Rows of 2D distribution grid: " << HICMA_descriptorC->p) + LOGGER(" Number of Rows of 2D distribution grid: " << HICMA_descriptorC->q) + + LOGGER(" Is Matrix Not Fit in Memory: " << HICMA_descriptorC->ooc) + LOGGER(" Size including Padding: " << HICMA_descriptorC->bsiz) + + // Print Data Matrix of Descriptor + LOGGER("** Data in Matrix:") + LOGGER_2("", 0) + auto *data_mat = (double *) HICMA_descriptorC->mat; + for (int i = 0; i < config_problem_size; ++i) { + LOGGER_PRECISION_1(" " << data_mat[i], 0) + } + + delete kernel; +} diff --git a/examples/hardware/CMakeLists.txt b/examples/hardware/CMakeLists.txt new file mode 100644 index 00000000..3a0e5eb6 --- /dev/null +++ b/examples/hardware/CMakeLists.txt @@ -0,0 +1,16 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.0.1 +# @author Mahmoud ElKarargy +# @date 2024-02-14 + +# Define the target executable +add_executable(Example_Hardware ${CMAKE_CURRENT_SOURCE_DIR}/ExaGeoStatHardware.cpp) + +# Link the target executable with the project and any additional libraries +target_link_libraries(Example_Hardware PRIVATE ${PROJECT_NAME}_INTERFACE) + diff --git a/examples/hardware/ExaGeoStatHardware.cpp b/examples/hardware/ExaGeoStatHardware.cpp new file mode 100644 index 00000000..520f3a39 --- /dev/null +++ b/examples/hardware/ExaGeoStatHardware.cpp @@ -0,0 +1,31 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestExaGeoStatHardware.cpp + * @briefExample for the ExaGeoStatHardware class in the ExaGeoStat software package. + * @version 1.0.1 + * @author Mahmoud ElKarargy + * @date 2024-02-14 +**/ + +#include +#include +#include + +using namespace exageostat::common; +using namespace exageostat::hardware; +using namespace exageostat::configurations; + +int main(int argc, char **argv) { + LOGGER("** Example of Hardware **") + + // Initialize Configuration + Configurations configuration; + configuration.InitializeArguments(argc,argv); + + // Initialize Hardware + auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), configuration.GetGPUsNumbers()); +} \ No newline at end of file diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index e7507436..579b7171 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -57,6 +57,16 @@ */ #define Q_NORM 1.959964 +/** + * Kernel Files Path Definition + */ +#define KERNELS_PATH PROJECT_SOURCE_DIR "/inst/include/kernels/concrete/" + +/** + * Logging Path Definition + */ +#define LOG_PATH PROJECT_SOURCE_DIR "/synthetic_ds/" + namespace exageostat::common { /** @@ -281,8 +291,6 @@ namespace exageostat::common { // This set stores the kernel names. std::set kernelNames; // This string stores the directory path where the kernel files are located. - // The path is obtained by using the __FILE__ macro to get the full path of the current source file, - // and then navigating two levels up to reach the directory that contains the kernel files. const std::string directoryPath = KERNELS_PATH; // This loop iterates through all the files in the directory and extracts the kernel names. for (const auto &entry: std::filesystem::directory_iterator(directoryPath)) { diff --git a/src/runtime/starpu/CMakeLists.txt b/src/runtime/starpu/CMakeLists.txt new file mode 100644 index 00000000..f08891df --- /dev/null +++ b/src/runtime/starpu/CMakeLists.txt @@ -0,0 +1,6 @@ +add_subdirectory(concrete) +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/StarPuFunctions.cpp + ${SOURCES} + PARENT_SCOPE +) diff --git a/tests/heavy-tests/ExamplesTests.cpp b/tests/heavy-tests/ExamplesTests.cpp index 7a9774ed..f87b46ec 100644 --- a/tests/heavy-tests/ExamplesTests.cpp +++ b/tests/heavy-tests/ExamplesTests.cpp @@ -95,4 +95,3 @@ TEST_CASE("EXAMPLES") { } } } - From 7c6f095ac5ec41128dac3a3d40c47d42572f28ee Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 3 Mar 2024 15:29:25 +0200 Subject: [PATCH 36/82] R branch init --- .gitignore | 2 + CMakeLists.txt | 24 +- DESCRIPTION | 24 + ExaGeoStatCPPConfig.cmake.in | 2 +- Jenkinsfile | 6 +- NAMESPACE | 3 + R/ExaGeoStatCPP.R | 1 + USER_MANUAL.md | 4 +- clean_build.sh | 6 +- cleanup | 28 + .../FindPkgconfigLibrariesAbsolutePath.cmake | 2 +- cmake/FindR.cmake | 172 ++++++ cmake/ImportBLAS.cmake | 2 +- cmake/ImportBLASPP.cmake | 35 +- cmake/ImportCatch2.cmake | 2 +- cmake/ImportChameleon.cmake | 2 +- cmake/ImportGSL.cmake | 2 +- cmake/ImportHCore.cmake | 2 +- cmake/ImportHiCMA.cmake | 2 +- cmake/ImportHwloc.cmake | 2 +- cmake/ImportLapack.cmake | 2 +- cmake/ImportNLOPT.cmake | 2 +- cmake/ImportStarPu.cmake | 2 +- cmake/ImportStarsH.cmake | 2 +- cmake/macros/BuildDependency.cmake | 7 +- cmake/macros/ImportDependency.cmake | 3 +- cmake/toolchains/CudaToolchain.cmake | 2 +- cmake/toolchains/GccToolchain.cmake | 2 +- config.sh | 172 ------ configure | 263 +++++++++ docs/CMakeLists.txt | 2 +- examples/CMakeLists.txt | 2 +- examples/configurations/CMakeLists.txt | 2 +- .../RunningWithDifferentConfigurations.cpp | 8 +- .../configurations/SetupConfigurations.cpp | 7 +- examples/data-generators/CMakeLists.txt | 2 +- .../SyntheticDataGeneration.cpp | 6 +- examples/data-loader/CSVLoader.cpp | 3 - examples/descriptors/ChameleonDescriptor.cpp | 4 +- .../descriptors/ChameleonToHicmaConverter.cpp | 4 +- examples/descriptors/HicmaDescriptor.cpp | 4 +- examples/end-to-end/CMakeLists.txt | 2 +- examples/end-to-end/DataGeneration.cpp | 10 +- .../end-to-end/DataGenerationAndModeling.cpp | 10 +- .../DataGenerationModelingAndPrediction.cpp | 10 +- examples/end-to-end/DataModeling.cpp | 8 +- examples/end-to-end/DataPrediction.cpp | 8 +- .../Rcpp-adapters/FunctionsAdapter.hpp | 153 +++++ inst/include/api/ExaGeoStat.hpp | 22 +- inst/include/common/Definitions.hpp | 5 +- inst/include/common/PluginRegistry.hpp | 4 +- inst/include/common/Utils.hpp | 106 ---- .../include/configurations/Configurations.hpp | 530 +++++++++--------- .../include/data-generators/DataGenerator.hpp | 10 +- .../data-generators/LocationGenerator.hpp | 3 +- .../concrete/SyntheticGenerator.hpp | 8 +- inst/include/data-loader/DataLoader.hpp | 15 +- .../data-loader/concrete/CSVLoader.hpp | 4 +- inst/include/data-units/DescriptorData.hpp | 2 +- inst/include/data-units/ExaGeoStatData.hpp | 160 +++--- inst/include/data-units/Locations.hpp | 2 +- .../data-units/ModelingDataHolders.hpp | 12 +- .../descriptor/ExaGeoStatDescriptor.hpp | 2 +- .../concrete/ChameleonDescriptor.hpp | 2 +- .../descriptor/concrete/HicmaDescriptor.hpp | 2 +- inst/include/hardware/ExaGeoStatHardware.hpp | 95 ++-- inst/include/helpers/BasselFunction.hpp | 2 +- inst/include/helpers/ByteHandler.hpp | 2 +- inst/include/helpers/CommunicatorMPI.hpp | 2 +- inst/include/helpers/DiskWriter.hpp | 4 +- .../helpers/DistanceCalculationHelpers.hpp | 2 +- inst/include/kernels/Kernel.hpp | 2 +- .../concrete/BivariateMaternFlexible.hpp | 2 +- .../concrete/BivariateMaternParsimonious.hpp | 2 +- .../BivariateSpacetimeMaternStationary.hpp | 2 +- .../concrete/TrivariateMaternParsimonious.hpp | 2 +- .../concrete/UnivariateExpNonGaussian.hpp | 2 +- .../concrete/UnivariateMaternDbeta.hpp | 2 +- .../concrete/UnivariateMaternDdbetaBeta.hpp | 2 +- .../concrete/UnivariateMaternDdbetaNu.hpp | 2 +- .../concrete/UnivariateMaternDdnuNu.hpp | 2 +- .../UnivariateMaternDdsigmaSquare.hpp | 2 +- .../UnivariateMaternDdsigmaSquareBeta.hpp | 2 +- .../UnivariateMaternDdsigmaSquareNu.hpp | 2 +- .../kernels/concrete/UnivariateMaternDnu.hpp | 2 +- .../concrete/UnivariateMaternDsigmaSquare.hpp | 2 +- .../concrete/UnivariateMaternNonGaussian.hpp | 2 +- .../UnivariateMaternNuggetsStationary.hpp | 2 +- .../concrete/UnivariateMaternStationary.hpp | 2 +- .../UnivariateSpacetimeMaternStationary.hpp | 2 +- .../LinearAlgebraFactory.hpp | 2 +- .../LinearAlgebraMethods.hpp | 58 +- .../concrete/ChameleonHeaders.hpp | 2 +- .../concrete/HicmaHeaders.hpp | 2 +- .../chameleon/ChameleonImplementation.hpp | 6 +- .../chameleon/dense/ChameleonDense.hpp | 2 +- .../concrete/chameleon/dst/ChameleonDST.hpp | 2 +- .../hicma/tlr/HicmaImplementation.hpp | 12 +- inst/include/prediction/Prediction.hpp | 12 +- .../PredictionAuxiliaryFunctions.hpp | 2 +- inst/include/prediction/PredictionHelpers.hpp | 6 +- inst/include/results/Results.hpp | 2 +- inst/include/utilities/EnumStringParser.hpp | 66 +++ inst/include/utilities/ErrorHandler.hpp | 131 +++++ inst/include/utilities/Logger.hpp | 132 +++++ inst/include/utilities/Printer.hpp | 38 ++ scripts/Benchmarking.sh | 2 +- src/CMakeLists.txt | 39 +- src/Rcpp-adapters/CMakeLists.txt | 17 + src/Rcpp-adapters/FunctionsAdapter.cpp | 292 ++++++++++ src/Rcpp-adapters/RcppExports.cpp | 45 ++ src/Rcpp-adapters/RcppModules.cpp | 75 +++ src/api/CMakeLists.txt | 2 +- src/api/ExaGeoStat.cpp | 14 +- src/configurations/CMakeLists.txt | 2 +- src/configurations/Configurations.cpp | 47 +- src/data-generators/CMakeLists.txt | 2 +- src/data-generators/DataGenerator.cpp | 25 +- src/data-generators/LocationGenerator.cpp | 2 +- src/data-generators/concrete/CMakeLists.txt | 2 +- .../concrete/SyntheticGenerator.cpp | 11 +- src/data-loader/DataLoader.cpp | 6 +- src/data-loader/concrete/CMakeLists.txt | 2 +- src/data-loader/concrete/CSVLoader.cpp | 5 +- src/data-units/CMakeLists.txt | 2 +- src/data-units/DescriptorData.cpp | 2 +- src/data-units/ExaGeoStatData.cpp | 20 +- src/data-units/Locations.cpp | 2 +- src/data-units/descriptor/CMakeLists.txt | 2 +- .../descriptor/ExaGeoStatDescriptor.cpp | 2 +- .../descriptor/concrete/CMakeLists.txt | 2 +- .../concrete/ChameleonDescriptor.cpp | 2 +- .../descriptor/concrete/HicmaDescriptor.cpp | 2 +- src/hardware/CMakeLists.txt | 2 +- src/hardware/ExaGeoStatHardware.cpp | 58 +- src/helpers/BasselFunction.cpp | 2 +- src/helpers/ByteHandler.cpp | 2 +- src/helpers/CMakeLists.txt | 2 +- src/helpers/CommunicatorMPI.cpp | 2 +- src/helpers/DiskWriter.cpp | 2 +- src/helpers/DistanceCalculationHelpers.cpp | 2 +- src/kernels/CMakeLists.txt | 2 +- src/kernels/Kernel.cpp | 2 +- .../concrete/BivariateMaternFlexible.cpp | 2 +- .../concrete/BivariateMaternParsimonious.cpp | 2 +- .../BivariateSpacetimeMaternStationary.cpp | 2 +- .../concrete/TrivariateMaternParsimonious.cpp | 2 +- .../concrete/UnivariateExpNonGaussian.cpp | 2 +- .../concrete/UnivariateMaternDbeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaBeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaNu.cpp | 2 +- .../concrete/UnivariateMaternDdnuNu.cpp | 2 +- .../UnivariateMaternDdsigmaSquare.cpp | 2 +- .../UnivariateMaternDdsigmaSquareBeta.cpp | 2 +- .../UnivariateMaternDdsigmaSquareNu.cpp | 2 +- src/kernels/concrete/UnivariateMaternDnu.cpp | 2 +- .../concrete/UnivariateMaternDsigmaSquare.cpp | 2 +- .../concrete/UnivariateMaternNonGaussian.cpp | 2 +- .../UnivariateMaternNuggetsStationary.cpp | 2 +- .../concrete/UnivariateMaternStationary.cpp | 2 +- .../UnivariateSpacetimeMaternStationary.cpp | 2 +- src/linear-algebra-solvers/CMakeLists.txt | 2 +- .../LinearAlgebraFactory.cpp | 3 +- .../LinearAlgebraMethods.cpp | 36 +- .../concrete/CMakeLists.txt | 2 +- .../chameleon/ChameleonImplementation.cpp | 7 +- .../chameleon/dense/ChameleonDense.cpp | 2 +- .../concrete/chameleon/dst/ChameleonDST.cpp | 2 +- .../concrete/tlr/HicmaImplementation.cpp | 14 +- src/prediction/CMakeLists.txt | 2 +- src/prediction/Prediction.cpp | 11 +- .../PredictionAuxiliaryFunctions.cpp | 4 +- src/prediction/PredictionHelpers.cpp | 5 +- src/results/CMakeLists.txt | 2 +- src/results/Results.cpp | 12 +- tests/R-tests/TestExaGeoStat.R | 115 ++++ tests/cpp-tests/CMakeLists.txt | 6 +- tests/cpp-tests/Rcpp-adapters/CMakeLists.txt | 16 + .../Rcpp-adapters/TestAllRFunctions.cpp | 46 ++ tests/cpp-tests/api/CMakeLists.txt | 2 +- tests/cpp-tests/api/TestExaGeoStatApi.cpp | 15 +- tests/cpp-tests/configurations/CMakeLists.txt | 2 +- .../configurations/TestConfigurations.cpp | 3 +- .../cpp-tests/data-generators/CMakeLists.txt | 2 +- .../concrete/TestCSVDataGenerator.cpp | 12 +- .../concrete/TestSyntheticGenerator.cpp | 7 +- tests/cpp-tests/data-units/CMakeLists.txt | 2 +- .../data-units/TestDescriptorData.cpp | 4 +- tests/cpp-tests/hardware/CMakeLists.txt | 2 +- .../hardware/TestExaGeoStatHardware.cpp | 4 +- tests/cpp-tests/helpers/CMakeLists.txt | 2 +- tests/cpp-tests/helpers/TestDiskWriter.cpp | 2 +- .../TestDistanceCalculationHelpers.cpp | 2 +- .../concrete/TestBivariateMaternFlexible.cpp | 6 +- .../TestBivariateMaternParsimonious.cpp | 9 +- ...TestBivariateSpacetimeMaternStationary.cpp | 6 +- .../TestTrivariateMaternParsimonious.cpp | 6 +- .../concrete/TestUnivariateExpNonGaussian.cpp | 2 +- .../concrete/TestUnivariateMaternDbeta.cpp | 4 +- .../TestUnivariateMaternDdbetaBeta.cpp | 3 +- .../concrete/TestUnivariateMaternDdbetaNu.cpp | 4 +- .../concrete/TestUnivariateMaternDdnuNu.cpp | 3 +- .../TestUnivariateMaternDdsigmaSquare.cpp | 7 +- .../TestUnivariateMaternDdsigmaSquareBeta.cpp | 4 +- .../TestUnivariateMaternDdsigmaSquareNu.cpp | 3 +- .../concrete/TestUnivariateMaternDnu.cpp | 3 +- .../TestUnivariateMaternDsigmaSquare.cpp | 3 +- .../TestUnivariateMaternNonGaussian.cpp | 6 +- .../TestUnivariateMaternNuggetsStationary.cpp | 6 +- .../TestUnivariateMaternStationary.cpp | 6 +- .../TestUnivariatePowExpStationary.cpp | 4 +- ...estUnivariateSpacetimeMaternStationary.cpp | 6 +- .../linear-algebra-solvers/CMakeLists.txt | 2 +- .../TestChameleonImplementationDST.cpp | 4 +- .../TestChameleonImplementationDense.cpp | 4 +- .../concrete/TestHiCMAImplementationTLR.cpp | 8 +- tests/cpp-tests/prediction/CMakeLists.txt | 2 +- tests/cpp-tests/prediction/TestPrediction.cpp | 21 +- .../prediction/TestPredictionHelpers.cpp | 2 +- tests/cpp-tests/results/CMakeLists.txt | 2 +- tests/cpp-tests/results/TestResults.cpp | 2 +- tests/heavy-tests/CMakeLists.txt | 2 +- tests/heavy-tests/ExamplesTests.cpp | 3 +- tests/heavy-tests/HeavyTests.cpp | 3 +- 224 files changed, 2539 insertions(+), 1227 deletions(-) create mode 100755 DESCRIPTION create mode 100644 NAMESPACE create mode 100755 R/ExaGeoStatCPP.R create mode 100755 cleanup create mode 100644 cmake/FindR.cmake delete mode 100755 config.sh create mode 100755 configure create mode 100644 inst/include/Rcpp-adapters/FunctionsAdapter.hpp delete mode 100644 inst/include/common/Utils.hpp create mode 100644 inst/include/utilities/EnumStringParser.hpp create mode 100644 inst/include/utilities/ErrorHandler.hpp create mode 100644 inst/include/utilities/Logger.hpp create mode 100644 inst/include/utilities/Printer.hpp create mode 100644 src/Rcpp-adapters/CMakeLists.txt create mode 100644 src/Rcpp-adapters/FunctionsAdapter.cpp create mode 100644 src/Rcpp-adapters/RcppExports.cpp create mode 100644 src/Rcpp-adapters/RcppModules.cpp create mode 100644 tests/R-tests/TestExaGeoStat.R create mode 100644 tests/cpp-tests/Rcpp-adapters/CMakeLists.txt create mode 100644 tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp diff --git a/.gitignore b/.gitignore index d23b41d2..ba44e88c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ installdir/ .Rhistory .RData .RDataTmp +..Rcheck +src/symbols.rds # tar.gz files *.tar.gz diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fca23fc..ff95aa91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,10 +8,10 @@ # The project is a parallel high performance unified framework for geographical statistics on manycore systems. # The file sets up variables and finds dependencies required for the project. # It also provides options to enable building tests, building examples, building documentation, and enabling a packaging system for distribution. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah -# @date 2023-01-30 +# @date 2024-02-04 # Set the minimum CMake version required to 3.20 cmake_minimum_required(VERSION 3.20 FATAL_ERROR) @@ -24,11 +24,18 @@ option(BUILD_TESTS "Option to enable building tests" OFF) option(BUILD_HEAVY_TESTS "Option to enable building heavy tests, This may take a lot of time" OFF) option(BUILD_EXAMPLES "Option to enable building examples" ON) option(BUILD_DOCS "Build documentation in docs directory" ON) +option(USE_R "Enable the use of R and Rcpp in the project" OFF) option(CREATE_PACKAGE "Enable a packaging system for distribution" OFF) # Cmake Module Paths set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") +if (${BUILD_SHARED_LIBS}) + set(BLA_STATIC OFF) +else () + set(BLA_STATIC ON) +endif () + # Select toolchain based on whether CUDA is enabled or not if (USE_CUDA) message("") @@ -149,6 +156,19 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prerequisites) set(MY_LOGGER_PATH ${CMAKE_CURRENT_SOURCE_DIR}) add_definitions(-DMY_LOGGER_PATH="${CMAKE_CURRENT_SOURCE_DIR}") +if (USE_R) + message("") + message("---------------------------------------- Rcpp") + # Find R and Rcpp using FindR Module + find_package(R REQUIRED) + if (${R_FOUND}) + message(STATUS "Using R technology") + list(APPEND LIBS R) + add_definitions(-DUSING_R) + endif () +endif () + + # Add src Directory to expose added libraries add_subdirectory(src) diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100755 index 00000000..bafc2a8f --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,24 @@ +Package: ExaGeoStatCPP +Type: Package +Title: R Package Demonstrates the R/C++ Language Interface for Exascale GeoStatistics software +Version: 1.1.0 +Date: 2024-01-14 +Author: Mahmoud ElKarargy [aut, cph], Sameh Abdulah [cre, cph], KAUST King Abdullah University of Science and Technology [fnd, cph], Brightskies [cph] +Maintainer: Sameh Abdulah +Description: An R-wrapper for ExaGeoStatCPP: a parallel high performance unified framework for geostatistics on manycore systems. Its abbreviation stands for Exascale Geostatistics. The framework aims at optimizing the likelihood function for a given spatial data to provide an efficient way to predict missing observations. The framework targets many-core systems: clusters of CPUs and GPUs. +License: GPL (>= 3) +Imports: assertthat (>= 0.2.1), MASS, methods, Rcpp (>= 1.0.9) +Depends: R (>= 3.5.0), assertthat (>= 0.2.1), MASS +RoxygenNote: 7.2.3 +SystemRequirements: C++ (>= 11), lapacke (https://github.com/xianyi/OpenBLAS/releases) +NeedsCompilation: yes +OS_type: unix +Authors@R: c( + person("Mahmoud", "ElKarargy", role=c("aut", "cph"), email="mahmoud.elkarargy@brightskiesinc.com"), + person("Sameh", "Abdulah", role=c("cre","cph"), email="sameh.abdulah@kaust.edu.sa"), + person("KAUST", "King Abdullah University of Science and Technology", role=c("fnd","cph")), + person("Brightskies", role=c("cph")) + ) +URL: https://www.github.com/ecrc/ExaGeoStatCPP +BugReports: https://github.com/ecrc/ExaGeoStatCPP/issues +Encoding: UTF-8 \ No newline at end of file diff --git a/ExaGeoStatCPPConfig.cmake.in b/ExaGeoStatCPPConfig.cmake.in index ef8a1c2c..4e7ec663 100644 --- a/ExaGeoStatCPPConfig.cmake.in +++ b/ExaGeoStatCPPConfig.cmake.in @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file ExaGeoStatCPPConfig.cmake.in -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-30 diff --git a/Jenkinsfile b/Jenkinsfile index 353913a8..274dd7d3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,7 +27,7 @@ pipeline { module load mkl/2020.0.166 #################################################### set -x - ./config.sh -t -e + ./configure -t -e ./clean_build.sh ''' } @@ -69,7 +69,7 @@ pipeline { module load mkl/2020.0.166 #################################################### set -x - ./config.sh -t -e -H + ./configure -t -e -H ./clean_build.sh ''' } @@ -107,7 +107,7 @@ pipeline { # BLAS/LAPACK #################################################### module load mkl/2020.0.166 - ./config.sh -e + ./configure -e ./clean_build.sh cd bin make docs diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 00000000..7f899c19 --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,3 @@ +useDynLib(ExaGeoStatCPP, .registration=TRUE) +exportPattern("^[[:alpha:]]+") +import(methods, Rcpp) \ No newline at end of file diff --git a/R/ExaGeoStatCPP.R b/R/ExaGeoStatCPP.R new file mode 100755 index 00000000..c2ac7275 --- /dev/null +++ b/R/ExaGeoStatCPP.R @@ -0,0 +1 @@ +loadModule("ExaGeoStatCPP", TRUE) diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 20a6a485..f0e6b849 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -243,7 +243,7 @@ can be used to train the software to predict the values of new data better. After you provide your arguments with the Configurations module, you must do the following two steps: ```c++ // Create a new ExaGeoStat data that holds the locations and descriptors data. -std::unique_ptr> data; +std::unique_ptr> data; // Generate data by passing your arguments through the configurations, hardware, and container of the data, which will be filled with the newly generated data. ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); @@ -257,7 +257,7 @@ ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); - Then do the following two steps. ```c++ // Create a new ExaGeoStat data that holds the locations data and descriptors data. -std::unique_ptr> data; +std::unique_ptr> data; // Generate data by passing your arguments through the configurations, your hardware and your container of the data which will be filled with the new generated data. ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); ``` diff --git a/clean_build.sh b/clean_build.sh index d1f1a1d2..7f554073 100755 --- a/clean_build.sh +++ b/clean_build.sh @@ -1,11 +1,11 @@ -#!/bin/bash +#! /bin/sh # Copyright (c) 2017-2023 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file clean_build.sh # @brief This script cleans and builds a software package called ExaGeoStat. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-30 @@ -55,7 +55,7 @@ cd bin/ || { } # Clean the directory and build the code with the specified options. -cmake --build . $num_proc $verbose +cmake --build . "$num_proc" $verbose # Install the software if the -i option is provided. if [ "$installation" -eq 1 ]; then diff --git a/cleanup b/cleanup new file mode 100755 index 00000000..01ed4d6d --- /dev/null +++ b/cleanup @@ -0,0 +1,28 @@ +#! /bin/sh + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file cleanup +# @brief This file is intended to be used during 'R CMD build' command to delete all unnecessary files to match to CRAN policies. +# @version 1.1.0 +# @author David Helmy +# @author Mahmoud Elkarargy +# @date 2024-02-09 + +rm -rf ./src/*.so* +rm -rf ./src/*.d +rm -rf ./src/*.dll +rm -rf ./.idea/ +rm -rf ./cmake-build-debug +rm -rf ./bin/ +rm -rf ./.git/ +rm -rf ./config.sh +rm -rf ./clean_build.sh +rm -rf ./..Rcheck/ +rm -rf ./benchmarks/ +rm -rf ./doxygen_config +rm -rf ./prerequisites/ +rm -rf ./scripts/ +rm -rf ./installdir/ diff --git a/cmake/FindPkgconfigLibrariesAbsolutePath.cmake b/cmake/FindPkgconfigLibrariesAbsolutePath.cmake index c1c94c1f..5eef4e0b 100644 --- a/cmake/FindPkgconfigLibrariesAbsolutePath.cmake +++ b/cmake/FindPkgconfigLibrariesAbsolutePath.cmake @@ -16,7 +16,7 @@ # Univ. of California Berkeley, # Univ. of Colorado Denver. # -# @version 1.0.0 +# @version 1.1.0 # @author Florent Pruvost # @date 06-04-2018 # diff --git a/cmake/FindR.cmake b/cmake/FindR.cmake new file mode 100644 index 00000000..a341c146 --- /dev/null +++ b/cmake/FindR.cmake @@ -0,0 +1,172 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file FindR.cmake +# @brief Find the R and Rcpp library, Set some helpful variables. +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @author David Helmy +# @date 2024-01-14 + + +# Usage: +# find_package(R [REQUIRED] [QUIET] ) +# +# It sets the following variables: +# R _FOUND ... true if R and Rcpp is found on the system +# R_LIBRARIES ... full path to R and Rcpp library +# R_INCLUDE_DIRS ... R and Rcpp include directory +# +# The following variables will be checked by the function +# R_ROOT_PATH ... if set, the R libraries are exclusively searched +# under this path +# R_LIB_PATH ... if set, the Rcpp libraries are exclusively searched +# under this path + +if (DEFINED ENV{R_HOME}) + set(R_ROOT_PATH "$ENV{R_HOME}") + +else () + execute_process(COMMAND R RHOME OUTPUT_VARIABLE R_HOME) + string(REGEX REPLACE "\n" "" R_HOME "${R_HOME}") + set(R_ROOT_PATH "${R_HOME}") + +endif () + +if (NOT R_INCLUDE_PATH) + execute_process(COMMAND ${R_ROOT_PATH}/bin/Rscript -e "cat(Sys.getenv('R_INCLUDE_DIR'))" OUTPUT_VARIABLE R_INCLUDE_DIR) + string(REGEX REPLACE "\n" "" R_INCLUDE_DIR "${R_INCLUDE_DIR}") + set(R_INCLUDE_PATH "${R_INCLUDE_DIR}") + message(STATUS "R Include Path : " ${R_INCLUDE_PATH}) +endif () + + +if (NOT RCPP_LIB_PATH) + execute_process( + COMMAND ${R_ROOT_PATH}/bin/Rscript -e "cat(find.package('Rcpp'))" + OUTPUT_VARIABLE RCPP_LIB_PATH + RESULT_VARIABLE RSCRIPT_RESULT + ) + + # Check if the command was successful + if (RSCRIPT_RESULT EQUAL 0) + # Trim whitespace from both ends of the output + string(STRIP ${RCPP_LIB_PATH} RCPP_LIB_PATH) + message(STATUS "RCPP_LIB_PATH: ${RCPP_LIB_PATH}") + else() + message(FATAL_ERROR "Error running Rscript. Exit code: ${RSCRIPT_RESULT}") + endif() +endif () + +message(STATUS "R Home Path : " ${R_ROOT_PATH}) + +if (R_ROOT_PATH) + + if (APPLE) + find_library( + R_DYN_LIB + NAMES "libR.dylib" + PATHS ${R_ROOT_PATH} + PATH_SUFFIXES "lib" "lib64" "bin" + NO_DEFAULT_PATH + ) + + else () + #find libs + find_library( + R_DYN_LIB + NAMES "libR.so" + PATHS ${R_ROOT_PATH} + PATH_SUFFIXES "lib" "lib64" "bin" + NO_DEFAULT_PATH + ) + + endif () + + if (R_DYN_LIB MATCHES R_DYN_LIB-NOTFOUND) + set(R_DYN_LIB "") + message("R is built with no dynamic library support") + endif () + + set(R_LIB + ${R_DYN_LIB} + ) + +else () + error("R is not installed ") +endif (R_ROOT_PATH) + + +if (R_INCLUDE_PATH) + # find includes + find_path( + R_INCLUDE_DIRS + REQUIRED + NAMES "R.h" + PATHS ${R_INCLUDE_PATH} + PATH_SUFFIXES "include" + NO_DEFAULT_PATH + ) +endif () + +if (RCPP_LIB_PATH) + #find libs + find_library( + RCPP_LIB + REQUIRED + NAMES "Rcpp.so" + PATHS ${RCPP_LIB_PATH} + PATH_SUFFIXES "/libs" "/lib64" "/bin" + NO_DEFAULT_PATH + ) + + # find includes + find_path( + RCPP_INCLUDE_DIRS + REQUIRED + NAMES "Rcpp.h" + PATHS ${RCPP_LIB_PATH} + PATH_SUFFIXES "/include" + NO_DEFAULT_PATH + ) + + +else () + message("Rcpp is not installed ...") +endif (RCPP_LIB_PATH) + +set(R_INCLUDE + ${R_INCLUDE} + ${R_INCLUDE_DIRS} + ${RCPP_INCLUDE_DIRS} + ) + +add_library(R INTERFACE IMPORTED) + +if (R_LIB) + set_target_properties(R + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${R_INCLUDE}" + INTERFACE_LINK_LIBRARIES "${R_LIB}" + IMPORTED_LOCATION ${RCPP_LIB} + ) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(R DEFAULT_MSG + R_INCLUDE R_LIB) + + include_directories(${R_INCLUDE}) + mark_as_advanced(R_INCLUDE R_INCLUDE_DIRS RCPP_INCLUDE_DIRS R_LIB RCPP_LIB) + +else () + set_target_properties(R + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${R_INCLUDE}" + IMPORTED_LOCATION ${RCPP_LIB} + ) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(R DEFAULT_MSG + R_INCLUDE) + + include_directories(${R_INCLUDE}) + mark_as_advanced(R_INCLUDE R_INCLUDE_DIRS RCPP_INCLUDE_DIRS RCPP_LIB) +endif () \ No newline at end of file diff --git a/cmake/ImportBLAS.cmake b/cmake/ImportBLAS.cmake index 72e6d460..28a9c901 100644 --- a/cmake/ImportBLAS.cmake +++ b/cmake/ImportBLAS.cmake @@ -4,7 +4,7 @@ # @file ImportBLAS.cmake # @brief This file searches for the BLAS library and includes it if not already included. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-03-12 diff --git a/cmake/ImportBLASPP.cmake b/cmake/ImportBLASPP.cmake index 1b2f7e29..ef008506 100644 --- a/cmake/ImportBLASPP.cmake +++ b/cmake/ImportBLASPP.cmake @@ -4,21 +4,44 @@ # @file ImportBLASPP.cmake # @brief This file searches for the BLAS++ library and includes it if not already included. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy -# @date 2023-03-12 +# @date 2024-02-04 include(ImportBLAS) #Configurations set(name blaspp) +string(TOUPPER ${name} capital_name) set(tag "v2023.01.00") set(url "https://github.com/icl-utk-edu/blaspp") -set(${name}_DIR "${CMAKE_INSTALL_PREFIX}/${capital_name}/lib/cmake/${name}") +set(${name}_DIR "${CMAKE_INSTALL_PREFIX}/${capital_name}/${name}-build/") -include(FetchContent) -FetchContent_Declare(${name} GIT_REPOSITORY "${url}" GIT_TAG "${tag}") -FetchContent_MakeAvailable(${name}) +message("") +message("---------------------------------------- ${capital_name}") +message(STATUS "Checking for ${capital_name} with Version ${version}") + +# Check if the target is already included +IF (NOT TARGET ${name}) + include(FindPkgConfig) + find_package(PkgConfig QUIET) + find_package(${name} ${version} QUIET COMPONENTS ${components}) + + # If the package is found, print a message + if (${name}_FOUND) + message(" Found ${capital_name}; ${${name}_DIR} ${${name}_LIBRARIES}") + else () + # If the package is not found, install it using BuildDependency + message(" Can't find ${capital_name}, Installing it instead ..") + include(FetchContent) + set(FETCHCONTENT_BASE_DIR ${CMAKE_INSTALL_PREFIX}/${capital_name}) + FetchContent_Declare(${name} GIT_REPOSITORY "${url}" GIT_TAG "${tag}") + FetchContent_MakeAvailable(${name}) + + endif () +else () + message(STATUS "${capital_name} already included") +endif () set(LIBS ${name} ${LIBS}) message(STATUS "${name} done") \ No newline at end of file diff --git a/cmake/ImportCatch2.cmake b/cmake/ImportCatch2.cmake index 1d255902..c00a6e29 100644 --- a/cmake/ImportCatch2.cmake +++ b/cmake/ImportCatch2.cmake @@ -5,7 +5,7 @@ # @file ImportChameleon.cmake # @brief This script checks for Chameleon and includes it in the project if it is not already a target. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-03-13 diff --git a/cmake/ImportChameleon.cmake b/cmake/ImportChameleon.cmake index a71ae88c..b1d47819 100644 --- a/cmake/ImportChameleon.cmake +++ b/cmake/ImportChameleon.cmake @@ -5,7 +5,7 @@ # @file ImportChameleon.cmake # @brief This script checks for Chameleon and includes it in the project if it is not already a target. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 diff --git a/cmake/ImportGSL.cmake b/cmake/ImportGSL.cmake index f6ebd0ff..a4010bab 100644 --- a/cmake/ImportGSL.cmake +++ b/cmake/ImportGSL.cmake @@ -5,7 +5,7 @@ # @file ImportGSL.cmake # @brief Checks for the GSL library and includes it in the project if it is not already present. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-16 diff --git a/cmake/ImportHCore.cmake b/cmake/ImportHCore.cmake index 5f1c53cf..42f26fa5 100644 --- a/cmake/ImportHCore.cmake +++ b/cmake/ImportHCore.cmake @@ -5,7 +5,7 @@ # @file ImportHCore.cmake # @brief Checks for the Hcore library and includes it in the project if it is not already present. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-15 diff --git a/cmake/ImportHiCMA.cmake b/cmake/ImportHiCMA.cmake index 7257ee15..e8b5e2ef 100644 --- a/cmake/ImportHiCMA.cmake +++ b/cmake/ImportHiCMA.cmake @@ -5,7 +5,7 @@ # @file ImportHiCMA.cmake # @brief Find and include HiCMA library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 diff --git a/cmake/ImportHwloc.cmake b/cmake/ImportHwloc.cmake index a4f71525..c0f22bbc 100644 --- a/cmake/ImportHwloc.cmake +++ b/cmake/ImportHwloc.cmake @@ -5,7 +5,7 @@ # @file ImportHwloc.cmake # @brief Find and include Hwloc library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-15 diff --git a/cmake/ImportLapack.cmake b/cmake/ImportLapack.cmake index 5a6a6c64..50586cd0 100644 --- a/cmake/ImportLapack.cmake +++ b/cmake/ImportLapack.cmake @@ -5,7 +5,7 @@ # @file ImportLapack.cmake # @brief Find and include LAPACK library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-12 diff --git a/cmake/ImportNLOPT.cmake b/cmake/ImportNLOPT.cmake index ac796b44..c99add7f 100644 --- a/cmake/ImportNLOPT.cmake +++ b/cmake/ImportNLOPT.cmake @@ -5,7 +5,7 @@ # @file ImportNLOPT.cmake # @brief Find and include NLOPT library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-26 diff --git a/cmake/ImportStarPu.cmake b/cmake/ImportStarPu.cmake index b160c574..24dbb773 100644 --- a/cmake/ImportStarPu.cmake +++ b/cmake/ImportStarPu.cmake @@ -5,7 +5,7 @@ # @file ImportSTARPU.cmake # @brief Find and include STARPU library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 diff --git a/cmake/ImportStarsH.cmake b/cmake/ImportStarsH.cmake index b7c842b6..c0c1ce93 100644 --- a/cmake/ImportStarsH.cmake +++ b/cmake/ImportStarsH.cmake @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief Find and include STARSH library as a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-13 diff --git a/cmake/macros/BuildDependency.cmake b/cmake/macros/BuildDependency.cmake index 825db78b..bc112db9 100644 --- a/cmake/macros/BuildDependency.cmake +++ b/cmake/macros/BuildDependency.cmake @@ -4,9 +4,10 @@ # @file BuildDependency.cmake # @brief Fetches, builds, and installs a dependency. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy -# @date 2023-03-12 +# @author Amr Nasr +# @date 2024-02-04 # After building and installing the dependency, the macro installs the lib, include, and share directories # in the current directory. @@ -59,7 +60,7 @@ macro(BuildDependency raw_name url tag flags is_using_cmake is_using_git auto_ge # Configure subproject. if (${is_using_cmake}) - execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/${capital_name} ${flags} + execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/${capital_name} -DCMAKE_C_FLAGS=-fPIC ${flags} ${${name}_srcpath} WORKING_DIRECTORY ${${name}_binpath}) else () diff --git a/cmake/macros/ImportDependency.cmake b/cmake/macros/ImportDependency.cmake index 470e22d7..b32580dd 100644 --- a/cmake/macros/ImportDependency.cmake +++ b/cmake/macros/ImportDependency.cmake @@ -4,8 +4,9 @@ # @file ImportDependency.cmake # @brief CMake script for importing and building external dependencies. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy +# @author Amr Nasr # @date 2023-12-28 # ImportDependency Macro: diff --git a/cmake/toolchains/CudaToolchain.cmake b/cmake/toolchains/CudaToolchain.cmake index aaf7ae59..a84a2666 100644 --- a/cmake/toolchains/CudaToolchain.cmake +++ b/cmake/toolchains/CudaToolchain.cmake @@ -5,7 +5,7 @@ # @file CudaToolchain.cmake # @brief This file is used to set up the CUDA toolchain for compilation. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-12 diff --git a/cmake/toolchains/GccToolchain.cmake b/cmake/toolchains/GccToolchain.cmake index d02499de..3fda7f79 100644 --- a/cmake/toolchains/GccToolchain.cmake +++ b/cmake/toolchains/GccToolchain.cmake @@ -4,7 +4,7 @@ # @file GccToolchain.cmake # @brief This file is used to set up the GCC toolchain for compilation. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-12 diff --git a/config.sh b/config.sh deleted file mode 100755 index 04ac419e..00000000 --- a/config.sh +++ /dev/null @@ -1,172 +0,0 @@ -#!/bin/bash -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# @file config.sh -# @version 1.0.1 -# @author Mahmoud ElKarargy -# @date 2023-01-30 - -# Set variables and default values -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -INSTALL_PREFIX=$PWD/installdir/_deps -PROJECT_SOURCE_DIR=$(dirname "$0") -BUILDING_TESTS="OFF" -BUILDING_HEAVY_TESTS="OFF" -BUILDING_EXAMPLES="OFF" -USING_HiCMA="OFF" -VERBOSE="OFF" -USE_CUDA="OFF" -USE_MPI="OFF" -BLAS_VENDOR="" -PACKAGE="OFF" -SHOW_WARNINGS="OFF" -COMPILE_FLAGS="-Wl,--no-as-needed" -DEVELOPER_WARNINGS="-Wno-dev" - - -for arg in "$@" -do - case $arg in - --use-mkl) - echo -e "${GREEN}MKL as a BLA vendor${NC}" - BLAS_VENDOR="Intel10_64lp" - shift # Remove --use-mkl from processing - ;; - esac -done - -# Parse command line options -while getopts ":tevhHi:cmpTw" opt; do - case $opt in - i) ##### Define installation path ##### - echo -e "${YELLOW}Installation path set to $OPTARG.${NC}" - INSTALL_PREFIX=$OPTARG - ;; - t) ##### Building tests enabled ##### - echo -e "${GREEN}Building tests enabled.${NC}" - BUILDING_TESTS="ON" - ;; - T) ##### Building heavy tests enabled ##### - echo -e "${GREEN}Building heavy tests enabled.${NC}" - BUILDING_HEAVY_TESTS="ON" - ;; - e) ##### Building examples enabled ##### - echo -e "${GREEN}Building examples enabled.${NC}" - BUILDING_EXAMPLES="ON" - ;; - H) ##### Using HiCMA ##### - echo -e "${GREEN}Using HiCMA.${NC}" - USING_HiCMA="ON" - ;; - c)##### Using cuda enabled ##### - echo -e "${GREEN}Cuda enabled ${NC}" - USE_CUDA="ON" - ;; - m)##### Using MPI enabled ##### - echo -e "${GREEN}MPI enabled ${NC}" - USE_MPI="ON" - ;; - v) ##### printing full output of make ##### - echo -e "${GREEN}printing make with details.${NC}" - VERBOSE="ON" - ;; - p) ##### Enabling packaging system for distribution ##### - echo -e "${GREEN}CPACK enabled${NC}" - PACKAGE=ON - ;; - w) ##### Enable showing all the warnings ##### - echo -e "${GREEN}Showing Warnings is enabled${NC}" - SHOW_WARNINGS="ON" - ;; - \?) ##### Error unknown option ##### - echo "Option $OPTARG parameter is unknown, please -h for help" - exit 1 - ;; - :) ##### Error in an option ##### - echo "Option $OPTARG requires parameter(s)" - exit 0 - ;; - h) ##### Prints the help ##### - echo "Usage of $(basename "$0"):" - echo "" - printf "%20s %s\n" "-i [path] :" "specify installation path, default = ${PWD}/installdir/_deps/" - printf "%20s %s\n" "-t :" "to enable building tests." - printf "%20s %s\n" "-T :" "to enable building heavy tests." - printf "%20s %s\n" "-e :" "to enable building examples." - printf "%20s %s\n" "-H :" "to enable using HiCMA." - printf "%20s %s\n" "-c :" "to enable using CUDA." - printf "%20s %s\n" "-m :" "to enable using MPI." - printf "%20s %s\n" "-v :" "to enable verbose printings." - printf "%20s %s\n" "-d :" "to enable debug mode." - printf "%20s %s\n" "-s :" "to manually pass MKL as your bla vendor." - printf "%20s %s\n" "-p :" "to enable a packaging system for distribution." - printf "%20s %s\n" "-h :" "Help." - echo "" - exit 1 - ;; - esac -done - -if [ -z "$BUILDING_TESTS" ]; then - BUILDING_TESTS="OFF" - echo -e "${RED}Building tests disabled.${NC}" -fi - -if [ -z "$BUILDING_EXAMPLES" ]; then - BUILDING_EXAMPLES="OFF" - echo -e "${RED}Building examples disabled.${NC}" -fi - -echo -e "${BLUE}Installation path set to $INSTALL_PREFIX.${NC}" - -if [ -z "$USING_HiCMA" ]; then - echo -e "${RED}Using HiCMA is disabled.${NC}" -fi - -if [ -z "$USE_CUDA" ]; then - USE_CUDA="OFF" - echo -e "${RED}Using CUDA disabled${NC}" -fi - -if [ -z "$USE_MPI" ]; then - USE_MPI="OFF" - echo -e "${RED}Using MPI disabled${NC}" -fi - -if [ "$SHOW_WARNINGS" = "ON" ]; then - COMPILE_FLAGS+=" -W" - DEVELOPER_WARNINGS="" -elif [ "$SHOW_WARNINGS" = "OFF" ]; then - COMPILE_FLAGS+=" -w" -fi - -echo "" -echo -e "${YELLOW}Use -h to print the usages of exageostat-cpp flags.${NC}" -echo "" -rm -rf bin/ -mkdir -p bin/ - -cmake "$DEVELOPER_WARNINGS" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DCMAKE_BUILD_TYPE=RELEASE \ - -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ - -DBUILD_TESTS="${BUILDING_TESTS}" \ - -DBUILD_HEAVY_TESTS="${BUILDING_HEAVY_TESTS}" \ - -DBUILD_EXAMPLES="${BUILDING_EXAMPLES}" \ - -DUSE_HICMA="${USING_HiCMA}" \ - -DCMAKE_VERBOSE_MAKEFILE:BOOL=${VERBOSE} \ - -DUSE_CUDA="${USE_CUDA}" \ - -DUSE_MPI="${USE_MPI}" \ - -DBLA_VENDOR="${BLAS_VENDOR}" \ - -DCREATE_PACKAGE="${PACKAGE}" \ - -H"${PROJECT_SOURCE_DIR}" \ - -B"${PROJECT_SOURCE_DIR}/bin" \ - -G "Unix Makefiles" \ - -DCMAKE_CXX_FLAGS_DEBUG="$COMPILE_FLAGS"\ - -DCMAKE_CXX_FLAGS_RELEASE="$COMPILE_FLAGS" diff --git a/configure b/configure new file mode 100755 index 00000000..bbc31fc9 --- /dev/null +++ b/configure @@ -0,0 +1,263 @@ +#! /bin/sh +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file config.sh +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @author David Helmy +# @date 2024-02-04 + +# Set variables and default values +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +INSTALL_PREFIX=$PWD/installdir/_deps + +# Function to install CMake from source +install_cmake() { + echo "CMake not found. Installing CMake from source..." + + # Create a temporary directory for building CMake + temp_dir=$(mktemp -d) + cd "$temp_dir" || exit 1 + + # Download CMake source code + wget https://github.com/Kitware/CMake/releases/download/v3.28.1/cmake-3.28.1.tar.gz + + # Extract the source code + tar -xzvf cmake-3.28.1.tar.gz + + # Enter the extracted directory + cd cmake-3.28.1 || exit 1 + + # Configure, build, and install CMake to the specified location + ./bootstrap --prefix="$INSTALL_PREFIX" --parallel=2 -- -DCMAKE_USE_OPENSSL=OFF + make -j 2 + sudo make install + + # Clean up + cd "$temp_dir" || exit 1 + rm -rf "$temp_dir" +} + +# shellcheck disable=SC2164 +cd "$(dirname "$0")" +# Get the operating system type using uname +OS_TYPE=$(uname) + +if [ "$OS_TYPE" = "darwin"* ]; then + ABSOLUTE_PATH=$([[ $1 == /* ]] && echo "$1" || echo "$PWD/${1#./}") +else + ABSOLUTE_PATH=$(dirname "$(realpath "$0")") +fi + + +BUILDING_TESTS="OFF" +BUILDING_HEAVY_TESTS="OFF" +BUILDING_EXAMPLES="OFF" +USING_HiCMA="OFF" +VERBOSE="OFF" +USE_CUDA="OFF" +USE_MPI="OFF" +BLAS_VENDOR="" +PACKAGE="OFF" +SHOW_WARNINGS="OFF" +COMPILE_FLAGS="-Wl,--no-as-needed -w -fpic" +DEVELOPER_WARNINGS="-Wno-dev" + +for arg in "$@" +do + case $arg in + --use-mkl) + echo "${GREEN}MKL as a BLA vendor${NC}" + BLAS_VENDOR="Intel10_64lp" + shift # Remove --use-mkl from processing + ;; + esac +done + +# Parse command line options +while getopts ":tevhHi:cmpTwr" opt; do + case $opt in + i) ##### Define installation path ##### + echo "${YELLOW}Installation path set to $OPTARG.${NC}" + INSTALL_PREFIX=$OPTARG + ;; + t) ##### Building tests enabled ##### + echo "${GREEN}Building tests enabled.${NC}" + BUILDING_TESTS="ON" + ;; + T) ##### Building heavy tests enabled ##### + echo "${GREEN}Building heavy tests enabled.${NC}" + BUILDING_HEAVY_TESTS="ON" + ;; + e) ##### Building examples enabled ##### + echo "${GREEN}Building examples enabled.${NC}" + BUILDING_EXAMPLES="ON" + ;; + H) ##### Using HiCMA ##### + echo "${GREEN}Using HiCMA.${NC}" + USING_HiCMA="ON" + ;; + c) ##### Using cuda enabled ##### + echo "${GREEN}Cuda enabled ${NC}" + USE_CUDA="ON" + ;; + m) ##### Using MPI enabled ##### + echo "${GREEN}MPI enabled ${NC}" + USE_MPI="ON" + ;; + v) ##### printing full output of make ##### + echo "${GREEN}printing make with details.${NC}" + VERBOSE="ON" + ;; + p) ##### Enabling packaging system for distribution ##### + echo "${GREEN}CPACK enabled${NC}" + PACKAGE=ON + ;; + w) ##### Enable showing all the warnings ##### + echo "${GREEN}Showing Warnings is enabled${NC}" + SHOW_WARNINGS="ON" + ;; + r) ##### Enable R and Rcpp support ##### + echo "${GREEN}R is enabled${NC}" + USE_R="ON" + ;; + \?) ##### Error unknown option ##### + echo "Option $OPTARG parameter is unknown, please -h for help" + exit 1 + ;; + :) ##### Error in an option ##### + echo "Option $OPTARG requires parameter(s)" + exit 0 + ;; + h) ##### Prints the help ##### + echo "Usage of $(basename "$0"):" + echo "" + printf "%20s %s\n" "-i [path] :" "specify installation path, default = ${PWD}/installdir/_deps/" + printf "%20s %s\n" "-t :" "to enable building tests." + printf "%20s %s\n" "-T :" "to enable building heavy tests." + printf "%20s %s\n" "-e :" "to enable building examples." + printf "%20s %s\n" "-H :" "to enable using HiCMA." + printf "%20s %s\n" "-c :" "to enable using CUDA." + printf "%20s %s\n" "-m :" "to enable using MPI." + printf "%20s %s\n" "-v :" "to enable verbose printings." + printf "%20s %s\n" "-d :" "to enable debug mode." + printf "%20s %s\n" "-r :" "to enable Rcpp support." + printf "%20s %s\n" "--use-mkl :" "to force the use of mkl as your bla vendor." + printf "%20s %s\n" "-p :" "to enable a packaging system for distribution." + printf "%20s %s\n" "-h :" "Help." + echo "" + exit 1 + ;; + esac +done + +if [ -z "$BUILDING_TESTS" ]; then + BUILDING_TESTS="OFF" + echo "${RED}Building tests disabled.${NC}" +fi + +if [ -z "$BUILDING_EXAMPLES" ]; then + BUILDING_EXAMPLES="OFF" + echo "${RED}Building examples disabled.${NC}" +fi + +echo "${BLUE}Installation path set to $INSTALL_PREFIX.${NC}" + +if [ -z "$USING_HiCMA" ]; then + echo "${RED}Using HiCMA is disabled.${NC}" +fi + +if [ -z "$USE_CUDA" ]; then + USE_CUDA="OFF" + echo "${RED}Using CUDA disabled${NC}" +fi + +if [ -z "$USE_MPI" ]; then + USE_MPI="OFF" + echo "${RED}Using MPI disabled${NC}" +fi + +if [ "$SHOW_WARNINGS" = "ON" ]; then + COMPILE_FLAGS="$COMPILE_FLAGS -W" + DEVELOPER_WARNINGS="" +elif [ "$SHOW_WARNINGS" = "OFF" ]; then + COMPILE_FLAGS="$COMPILE_FLAGS -w" +fi + +if [ -z "$USE_R" ]; then + USE_R="OFF" + echo "${RED}Using R is disabled${NC}" +fi + +echo "" +echo "${YELLOW}Use -h to print the usages of exageostat-cpp flags.${NC}" +echo "" + +# cleaning bin +rm -rf bin/ +mkdir bin/ + +# Check if cmake is installed +if command -v cmake /dev/null 2>&1; then + # Save the installation directory to the variable + cmake_install_dir=$(command -v cmake | xargs dirname) + echo "CMake is installed in: $cmake_install_dir" + cmake_command_bin=cmake +elif [ -x "/Applications/CMake.app/Contents/bin/cmake" ]; then + echo "CMake found in /Applications/CMake.app/Contents/bin/cmake." + cmake_install_dir="/Applications/CMake.app/Contents/bin" + cmake_command_bin="${cmake_install_dir}/cmake" +else + echo "Installing CMake from source" + mkdir "${ABSOLUTE_PATH}/inst/_deps/" + install_dir="${ABSOLUTE_PATH}/inst/_deps/" + install_cmake "$install_dir" + cmake_command_bin="${ABSOLUTE_PATH}/inst/_deps/bin/cmake" +fi + +"$cmake_command_bin" "$DEVELOPER_WARNINGS" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_BUILD_TYPE=RELEASE \ + -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ + -DBUILD_TESTS="${BUILDING_TESTS}" \ + -DBUILD_HEAVY_TESTS="${BUILDING_HEAVY_TESTS}" \ + -DBUILD_EXAMPLES="${BUILDING_EXAMPLES}" \ + -DUSE_HICMA="${USING_HiCMA}" \ + -DCMAKE_VERBOSE_MAKEFILE:BOOL=${VERBOSE} \ + -DUSE_CUDA="${USE_CUDA}" \ + -DUSE_MPI="${USE_MPI}" \ + -DUSE_R="${USE_R}" \ + -DBLA_VENDOR="${BLAS_VENDOR}" \ + -DCREATE_PACKAGE="${PACKAGE}" \ + -DBUILD_SHARED_LIBS=OFF \ + -H"${ABSOLUTE_PATH}" \ + -B"${ABSOLUTE_PATH}/bin" \ + -G "Unix Makefiles" \ + -DCMAKE_CXX_FLAGS_DEBUG="$COMPILE_FLAGS" \ + -DCMAKE_CXX_FLAGS_RELEASE="$COMPILE_FLAGS" + +if [ "$USE_R" = "ON" ]; then + # Change to the bin directory, or exit if it doesn't exist. + cd bin/ || { + echo "Error: bin directory not found." + exit 1 + } + + # Clean the directory and build the code with the specified options. + "$cmake_command_bin" --build . -j 2 + + if [ "$OS_TYPE" = "darwin"* ]; then + cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.dylib" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.dylib -> src" + else + cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.so" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.so -> src" + fi + + rm -rf "${ABSOLUTE_PATH:?}/bin/"* + +fi diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 39b38388..ca8e0208 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief Configures and generates documentation using Doxygen. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-03-12 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6bd804a5..7bc70451 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief Includes subdirectories for different modules of the ExaGeoStat software package. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-02-24 diff --git a/examples/configurations/CMakeLists.txt b/examples/configurations/CMakeLists.txt index 7c68b9ee..a3a22fc9 100644 --- a/examples/configurations/CMakeLists.txt +++ b/examples/configurations/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief Defines an executable and links it with the ExaGeoStat library and other libraries. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-31 diff --git a/examples/configurations/RunningWithDifferentConfigurations.cpp b/examples/configurations/RunningWithDifferentConfigurations.cpp index f8d8c94f..5962adae 100644 --- a/examples/configurations/RunningWithDifferentConfigurations.cpp +++ b/examples/configurations/RunningWithDifferentConfigurations.cpp @@ -6,17 +6,15 @@ /** * @file RunningWithDifferentConfigurations.cpp * @brief Demonstrates running ExaGeoStat with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-01-04 **/ -#include #include +#include -using namespace exageostat::configurations; using namespace exageostat::api; -using namespace exageostat::hardware; using namespace exageostat::dataunits; /** @@ -47,7 +45,7 @@ int main() { auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. - std::unique_ptr> data; + std::unique_ptr> data; ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); // Modeling module. ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); diff --git a/examples/configurations/SetupConfigurations.cpp b/examples/configurations/SetupConfigurations.cpp index bb1f3c4e..a06ca247 100644 --- a/examples/configurations/SetupConfigurations.cpp +++ b/examples/configurations/SetupConfigurations.cpp @@ -8,21 +8,20 @@ * @brief Demonstrates how to use the Configurations class from the ExaGeoStat software package. * @details This file demonstrates how to use the Configurations class from the ExaGeoStat software package * to obtain user-defined configurations. -* @version 1.0.0 +* @version 1.1.0 * @author Mahmoud ElKarargy -* @date 2023-01-31 +* @date 2024-02-04 * **/ #include -#include +#include #include using namespace std; using namespace exageostat::common; -using namespace exageostat::configurations; /** * @brief The main function of the program. diff --git a/examples/data-generators/CMakeLists.txt b/examples/data-generators/CMakeLists.txt index d90f9abe..eafd867d 100644 --- a/examples/data-generators/CMakeLists.txt +++ b/examples/data-generators/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief CMake file for building the Example_Synthetic_Data_Generation executable. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-03-04 diff --git a/examples/data-generators/SyntheticDataGeneration.cpp b/examples/data-generators/SyntheticDataGeneration.cpp index e9fa1465..214499f0 100644 --- a/examples/data-generators/SyntheticDataGeneration.cpp +++ b/examples/data-generators/SyntheticDataGeneration.cpp @@ -6,9 +6,9 @@ /** * @file SyntheticLocationsGeneration.cpp * @brief This file contains the main function for generating synthetic Locations for ExaGeoStat - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-03-04 + * @date 2024-02-04 **/ #include @@ -17,10 +17,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::generators; using namespace exageostat::common; -using namespace exageostat::hardware; using namespace exageostat::kernels; /** diff --git a/examples/data-loader/CSVLoader.cpp b/examples/data-loader/CSVLoader.cpp index 883221f0..47005a63 100644 --- a/examples/data-loader/CSVLoader.cpp +++ b/examples/data-loader/CSVLoader.cpp @@ -17,10 +17,8 @@ using namespace std; using namespace exageostat::kernels; -using namespace exageostat::hardware; using namespace exageostat::common; using namespace exageostat::generators; -using namespace exageostat::configurations; using namespace exageostat::dataLoader::csv; int main(int argc, char **argv) { @@ -44,7 +42,6 @@ int main(int argc, char **argv) { // Create a unique pointer to a DataGenerator object and generate data configurations.SetIsSynthetic(true); - configurations.SetIsCSV(false); unique_ptr> synthetic_generator = DataGenerator::CreateGenerator(configurations); auto data = synthetic_generator->CreateData(configurations, hardware, *kernel); diff --git a/examples/descriptors/ChameleonDescriptor.cpp b/examples/descriptors/ChameleonDescriptor.cpp index b09e0d44..13daf6ba 100644 --- a/examples/descriptors/ChameleonDescriptor.cpp +++ b/examples/descriptors/ChameleonDescriptor.cpp @@ -11,20 +11,18 @@ * @date 2024-02-14 **/ -#include #include #include #include #include +#include using namespace std; using namespace exageostat::common; using namespace exageostat::kernels; using namespace exageostat::plugins; -using namespace exageostat::hardware; using namespace exageostat::dataunits; -using namespace exageostat::configurations; int main(int argc, char **argv) { diff --git a/examples/descriptors/ChameleonToHicmaConverter.cpp b/examples/descriptors/ChameleonToHicmaConverter.cpp index 3cc7375d..03a1cfc9 100644 --- a/examples/descriptors/ChameleonToHicmaConverter.cpp +++ b/examples/descriptors/ChameleonToHicmaConverter.cpp @@ -11,20 +11,18 @@ * @date 2024-02-14 **/ -#include #include #include #include #include +#include using namespace std; using namespace exageostat::common; using namespace exageostat::kernels; using namespace exageostat::plugins; -using namespace exageostat::hardware; using namespace exageostat::dataunits; -using namespace exageostat::configurations; int main(int argc, char **argv) { diff --git a/examples/descriptors/HicmaDescriptor.cpp b/examples/descriptors/HicmaDescriptor.cpp index 5bea39c1..169dffac 100644 --- a/examples/descriptors/HicmaDescriptor.cpp +++ b/examples/descriptors/HicmaDescriptor.cpp @@ -11,20 +11,18 @@ * @date 2024-02-14 **/ -#include #include #include #include #include +#include using namespace std; using namespace exageostat::common; using namespace exageostat::kernels; using namespace exageostat::plugins; -using namespace exageostat::hardware; using namespace exageostat::dataunits; -using namespace exageostat::configurations; int main(int argc, char **argv) { diff --git a/examples/end-to-end/CMakeLists.txt b/examples/end-to-end/CMakeLists.txt index bfce4c30..9ed3a380 100644 --- a/examples/end-to-end/CMakeLists.txt +++ b/examples/end-to-end/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-31 diff --git a/examples/end-to-end/DataGeneration.cpp b/examples/end-to-end/DataGeneration.cpp index cc454722..f7c22d0a 100644 --- a/examples/end-to-end/DataGeneration.cpp +++ b/examples/end-to-end/DataGeneration.cpp @@ -7,17 +7,15 @@ * @file DataGeneration.cpp * @brief This program either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data. * @details The program takes command line arguments to configure the data generation. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-05-30 + * @date 2024-02-04 **/ -#include +#include #include -using namespace exageostat::configurations; using namespace exageostat::api; -using namespace exageostat::hardware; using namespace exageostat::dataunits; /** @@ -37,7 +35,7 @@ int main(int argc, char **argv) { auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. - std::unique_ptr> data; + std::unique_ptr> data; ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); return 0; diff --git a/examples/end-to-end/DataGenerationAndModeling.cpp b/examples/end-to-end/DataGenerationAndModeling.cpp index caf00a63..dd461e74 100644 --- a/examples/end-to-end/DataGenerationAndModeling.cpp +++ b/examples/end-to-end/DataGenerationAndModeling.cpp @@ -7,17 +7,15 @@ * @file DataGenerationAndModeling.cpp * @brief This program either generates synthetic data using the ExaGeoStat library, or reads an CSV file. * @details The program takes command line arguments to configure the data generation. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-06-21 + * @date 2024-02-04 **/ -#include +#include #include -using namespace exageostat::configurations; using namespace exageostat::api; -using namespace exageostat::hardware; using namespace exageostat::dataunits; /** @@ -37,7 +35,7 @@ int main(int argc, char **argv) { auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. - std::unique_ptr> data; + std::unique_ptr> data; ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); // Modeling module. ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); diff --git a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp index ef39d133..1c80a3b4 100644 --- a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp +++ b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp @@ -7,17 +7,15 @@ * @file DataGenerationModelingAndPrediction.cpp * @brief This program This program either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data, performs data modeling on loaded data, then predicts missing measurements using the ExaGeoStat library. * @details The program takes command line arguments to configure the data generation. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-06-21 + * @date 2024-02-04 **/ -#include +#include #include -using namespace exageostat::configurations; using namespace exageostat::api; -using namespace exageostat::hardware; using namespace exageostat::dataunits; /** @@ -37,7 +35,7 @@ int main(int argc, char **argv) { auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. - std::unique_ptr> data; + std::unique_ptr> data; ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); // Modeling module. ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); diff --git a/examples/end-to-end/DataModeling.cpp b/examples/end-to-end/DataModeling.cpp index 6a4b3fb2..be37b051 100644 --- a/examples/end-to-end/DataModeling.cpp +++ b/examples/end-to-end/DataModeling.cpp @@ -7,20 +7,18 @@ * @file DataModeling.cpp * @brief This program models data using the ExaGeoStat library. * @details The program takes command line arguments and example variables to configure the data modeling. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-06-21 + * @date 2024-02-04 **/ #include -#include +#include #include using namespace exageostat::api; using namespace exageostat::dataunits; -using namespace exageostat::configurations; -using namespace exageostat::hardware; /** * @brief Main entry point for the Data Modeling program. diff --git a/examples/end-to-end/DataPrediction.cpp b/examples/end-to-end/DataPrediction.cpp index 2f681566..e7df1d95 100644 --- a/examples/end-to-end/DataPrediction.cpp +++ b/examples/end-to-end/DataPrediction.cpp @@ -7,17 +7,15 @@ * @file DataPrediction.cpp * @brief This program predicts missing measurements using the ExaGeoStat library. * @details The program takes command line arguments to configure the data prediction module. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-09-11 + * @date 2024-02-04 **/ -#include +#include #include -using namespace exageostat::configurations; using namespace exageostat::api; -using namespace exageostat::hardware; using namespace exageostat::dataunits; /** diff --git a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp new file mode 100644 index 00000000..f570dd6d --- /dev/null +++ b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp @@ -0,0 +1,153 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file FunctionAdapter.hpp + * @brief Header file for function adapters in the ExaGeoStat software. + * @details It provides declarations for functions that adapt and initialize statistical models or algorithms based on user-defined configurations. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-29 +**/ + +#ifndef EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP +#define EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP + +#include +#include +#include +#include + +#include +#include +#include + +namespace exageostat::adapters { + + /** + * @brief Initializes and configures the R arguments for ExaGeoStat computations. + * @details This function prepares the necessary configurations required by ExaGeoStat to perform statistical computations. + * It includes setting up problem sizes, computational kernels, grid configurations, and other parameters essential + * for the execution of the ExaGeoStat algorithms. + * @param[in] aProblemSize The size of the problem to be solved. + * @param[in] aKernelName The name of the computational kernel to be used. + * @param[in] aTileSize A vector specifying the size of each tile in the computation. + * @param[in] aP_QGrid A vector defining the P x Q process grid. + * @param[in] aTimeSlot The time slot allocated for the computation. + * @param[in] aComputation The type of computation to be performed (e.g., estimation, prediction). + * @param[in] aPrecision The precision (e.g., single, double) of the computation. + * @param[in] aCoresGPUsNumber A vector specifying the number of CPU cores or GPUs to be used. + * @param[in] aBand The bandwidth of the problem, relevant for band-limited computations. + * @param[in] aMaxRank The maximum rank for low-rank approximations. + * @param[in] aInitialTheta A vector of initial values for the model parameters (theta). + * @param[in] aLowerUpperBounds A 2D vector specifying the lower and upper bounds for model parameters. + * @param[in] aEstimatedTheta A vector of estimated values for the model parameters after computation. + * @param[in] aVerbose A string indicating the verbosity level of the output. + * @param[in] aDimension The dimensionality of the problem (e.g., 2D, 3D). + * @param[in] aMaxMleIterations The maximum number of iterations for the Maximum Likelihood Estimation (MLE) algorithm. + * @param[in] aTolerance The tolerance threshold for convergence in iterative algorithms. + * @param[in] aPrediction A vector indicating prediction locations or settings. + * @return A pointer to a Configurations object containing the initialized settings. + * + */ + Configurations * + R_InitializeArguments(const int &aProblemSize, const std::string &aKernelName, const std::vector &aTileSize, + const std::vector &aP_QGrid, const int &aTimeSlot, const std::string &aComputation, + const std::string &aPrecision, const std::vector &aCoresGPUsNumber, const int &aBand, + const int &aMaxRank, const std::vector &aInitialTheta, + const std::vector> &aLowerUpperBounds, + const std::vector &aEstimatedTheta, const std::string &aVerbose, + const std::string &aDimension, const int &aMaxMleIterations, const double &aTolerance, + const std::vector &aPrediction, const std::vector &aPath, const bool &aSaveData + ); + + + /** + * @brief Retrieves X coordinates of locations from ExaGeoStat data. + * @details Extracts and returns the X coordinates of geographical or spatial locations stored in an ExaGeoStatData object, facilitating data manipulation and analysis within the ExaGeoStat framework. + * @param[in] apData Pointer to ExaGeoStatData object containing the spatial data. + * @return Numeric vector of X coordinates. + */ + Rcpp::NumericVector R_GetLocationX(ExaGeoStatData *apData); + + /** + * @brief Retrieves Y coordinates of locations from ExaGeoStat data. + * @details Extracts and returns the Y coordinates of geographical or spatial locations stored in an ExaGeoStatData object, supporting various spatial data analyses and operations within the ExaGeoStat software. + * @param[in] apData Pointer to ExaGeoStatData object containing the spatial data. + * @return Numeric vector of Y coordinates. + */ + Rcpp::NumericVector R_GetLocationY(ExaGeoStatData *apData); + + /** + * @brief Retrieves Z coordinates of locations from ExaGeoStat data. + * @details Extracts and returns the Z coordinates (elevation or depth) of spatial locations stored in an ExaGeoStatData object, enhancing three-dimensional spatial analysis capabilities within the ExaGeoStat framework. + * @param[in] apData Pointer to ExaGeoStatData object containing the spatial data. + * @return Numeric vector of Z coordinates. + */ + Rcpp::NumericVector R_GetLocationZ(ExaGeoStatData *apData); + + /** + * @brief Retrieves descriptive Z values from ExaGeoStat data based on type. + * @details Extracts and returns Z values from an ExaGeoStatData object, aiding in targeted spatial data analysis and visualization within ExaGeoStat. + * @param[in] apData Pointer to ExaGeoStatData object containing the spatial data. + * @param[in] aType String specifying the type of descriptor value to retrieve (e.g., "Chameleon", "HiCMA"). + * @return Numeric vector of descriptive Z values. + */ + Rcpp::NumericVector R_GetDescZValues(ExaGeoStatData *apData, const std::string &aType); + + /** + * @brief Function to load ExaGeoStat data. + * @details This function loads data into an ExaGeoStatData object using the provided hardware configuration and computational settings. + * It is designed to initialize the data structure necessary for subsequent statistical model operations within the ExaGeoStat framework. + * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. + * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. + * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data will be stored. + * @return A pointer to an ExaGeoStatData object containing the loaded data. + * + */ + ExaGeoStatData *R_ExaGeoStatLoadData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData); + /** + * @brief Function to model ExaGeoStat data. + * @details This function applies the model configurations specified in apConfigurations to the data stored in apData, using the hardware setup specified by apHardware. + * It prepares the ExaGeoStatData object for statistical analysis and prediction tasks, adjusting the internal data representations and parameters as needed. + * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. + * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. + * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data is stored and will be modeled. + * @param[in] aMeasurementsVector An optional Rcpp::Nullable object containing a vector of measurements. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsX An optional Rcpp::Nullable object containing a vector of X coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsY An optional Rcpp::Nullable object containing a vector of Y coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsZ An optional Rcpp::Nullable object containing a vector of Z coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @return A pointer to an ExaGeoStatData object containing the modeled data, ready for statistical analysis and prediction. + */ + ExaGeoStatData *R_ExaGeoStatModelData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData, + Rcpp::Nullable aMeasurementsVector = R_NilValue, + Rcpp::Nullable aLocationsX = R_NilValue, + Rcpp::Nullable aLocationsY = R_NilValue, + Rcpp::Nullable aLocationsZ = R_NilValue); + + /** + * @brief Function to predict using ExaGeoStat data. + * @details This function performs predictions based on the modeled ExaGeoStatData object. + * It utilizes the hardware configuration and computational settings defined by apHardware and apConfigurations, respectively, to execute prediction algorithms on the data stored in apData. + * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. + * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. + * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data is stored and will be modeled. + * @param[in] aMeasurementsVector An optional Rcpp::Nullable object containing a vector of measurements. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsX An optional Rcpp::Nullable object containing a vector of X coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsY An optional Rcpp::Nullable object containing a vector of Y coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsZ An optional Rcpp::Nullable object containing a vector of Z coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @return A pointer to an ExaGeoStatData object containing the modeled data, ready for statistical analysis and prediction. + */ + ExaGeoStatData *R_ExaGeoStatPredictData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData, + Rcpp::Nullable aMeasurementsVector = R_NilValue, + Rcpp::Nullable aLocationsX = R_NilValue, + Rcpp::Nullable aLocationsY = R_NilValue, + Rcpp::Nullable aLocationsZ = R_NilValue); + +} +#endif //EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP diff --git a/inst/include/api/ExaGeoStat.hpp b/inst/include/api/ExaGeoStat.hpp index 338713be..b2f93ad3 100644 --- a/inst/include/api/ExaGeoStat.hpp +++ b/inst/include/api/ExaGeoStat.hpp @@ -6,9 +6,9 @@ /** * @file ExaGeoStat.hpp * @brief High-Level Wrapper class containing the static API for ExaGeoStat operations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-05-30 + * @date 2024-02-04 **/ #ifndef EXAGEOSTATCPP_EXAGEOSTAT_HPP @@ -39,9 +39,9 @@ namespace exageostat::api { * @return void * */ - static void ExaGeoStatLoadData(const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfigurations, - std::unique_ptr> &aData); + static void ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, + Configurations &aConfigurations, + std::unique_ptr> &aData); /** * @brief Models Data whether it's synthetic data or real. @@ -52,9 +52,9 @@ namespace exageostat::api { * @return the last optimum value of MLE. * */ - static T ExaGeoStatDataModeling(const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, + static T ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, + Configurations &aConfigurations, + std::unique_ptr> &aData, T *apMeasurementsMatrix = nullptr); @@ -75,9 +75,9 @@ namespace exageostat::api { * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. * @return void */ - static void ExaGeoStatPrediction(const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, + static void ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, + Configurations &aConfigurations, + std::unique_ptr> &aData, T *apMeasurementsMatrix = nullptr); private: diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index 579b7171..65e91a68 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -5,7 +5,7 @@ /** * @file Definitions.hpp - * @version 1.0.1 + * @version 1.1.0 * @brief This file contains common definitions used in ExaGeoStat software package. * @details These definitions include enums for dimension, computation, precision, and floating point arithmetic; * A macro for instantiating template classes with supported types; and a set of available kernels. @@ -14,11 +14,10 @@ * @date 2023-03-21 **/ - #ifndef EXAGEOSTATCPP_DEFINITIONS_HPP #define EXAGEOSTATCPP_DEFINITIONS_HPP -//// TODO: This is a hot fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. +// This is a hot fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. #ifdef min #undef min #endif diff --git a/inst/include/common/PluginRegistry.hpp b/inst/include/common/PluginRegistry.hpp index 23df532f..f91c8fc1 100644 --- a/inst/include/common/PluginRegistry.hpp +++ b/inst/include/common/PluginRegistry.hpp @@ -6,7 +6,7 @@ /** * @file PluginRegistry.hpp * @brief Defines a template class for registering and creating plugins. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-04-30 **/ @@ -14,7 +14,7 @@ #ifndef EXAGEOSTATCPP_PLUGINREGISTRY_HPP #define EXAGEOSTATCPP_PLUGINREGISTRY_HPP -//// TODO: This is a hot fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. +// This is a hot fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. #ifdef min #undef min #endif diff --git a/inst/include/common/Utils.hpp b/inst/include/common/Utils.hpp deleted file mode 100644 index 357ba341..00000000 --- a/inst/include/common/Utils.hpp +++ /dev/null @@ -1,106 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file Utils.hpp - * @brief This file contains common functions used in ExaGeoStat software package. - * @details These functions include so far the VERBOSE macro that prints a message - * to the console if the verbosity setting is set to "verbose mode. - * @version 1.0.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @date 2023-03-21 -**/ - -#ifndef EXAGEOSTATCPP_UTILS_HPP -#define EXAGEOSTATCPP_UTILS_HPP - -#include -#include -#include - -#include -#include -#include - -/** - * DEFAULT_PRECISION the value of the default C++ std::cout number of precision. - */ -#define DEFAULT_PRECISION 6 - -/** -* Verbose macro for logging and debugging mode -*/ -#define VERBOSE(msg) \ - if(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::DETAILED_MODE && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) \ - std::cout << "\t\t\t " << msg << std::endl; - -/** - * LOGGER_1 macro for logging outputs with double taps and new line at the end. - */ - -#define LOGGER_1(msg) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) \ - std::cout << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg << std::endl; - -/** - * LOGGER_2 macro for logging outputs with double taps and without new line at the end. - */ -#define LOGGER_2(msg, A) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) \ - std::cout << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; - -/** - * LOGGER_CONTROL is The internal macro that simply strips the excess and ends up with the required macro - */ -#define LOGGER_CONTROL(x, A, B, FUNC, ...) FUNC - -/** - * LOGGER macro that's called, Used to logging outputs - */ -#define LOGGER(...) LOGGER_CONTROL(,##__VA_ARGS__, \ - LOGGER_2(__VA_ARGS__), \ - LOGGER_1(__VA_ARGS__), \ - ) - -/** -* LOGGER_PRECISION_1 macro for logging outputs without any taps, without new line at the end and with customized precision. -*/ -#define LOGGER_PRECISION_1(msg, precision) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) \ - std::cout << std::fixed << std::setprecision(precision) << msg; - -/** -* LOGGER_PRECISION macro for logging outputs without any taps, without new line at the end and with default C++ precision. -*/ -#define LOGGER_PRECISION_2(msg) \ - if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) \ - std::cout << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; - -/** - * LOGGER_CONTROL is The internal macro that simply strips the excess and ends up with the required macro - */ -#define LOGGER_PRECISION_CONTROL(x, A, B, FUNC, ...) FUNC - -/** - * LOGGER macro that's called, Used to logging outputs - */ -#define LOGGER_PRECISION(...) LOGGER_PRECISION_CONTROL(,##__VA_ARGS__, \ - LOGGER_PRECISION_1(__VA_ARGS__), \ - LOGGER_PRECISION_2(__VA_ARGS__), \ - ) - -/** - * Timing macro to start timing. - */ -#define START_TIMING(t) auto t##_start = std::chrono::high_resolution_clock::now() - -/** - * Timing macro to stop timing. - */ -#define STOP_TIMING(t) auto t##_end = std::chrono::high_resolution_clock::now(); \ - t = std::chrono::duration_cast>(t##_end - t##_start).count() - -#endif //EXAGEOSTATCPP_UTILS_HPP diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index f2e43f1e..e3c55831 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -5,11 +5,11 @@ /** * @file Configurations.hpp -* @version 1.0.1 +* @version 1.1.0 * @brief Contains the declaration of the Configurations class and its member functions. -* @author Sameh Abdulah * @author Mahmoud ElKarargy -* @date 2023-01-31 +* @author Sameh Abdulah +* @date 2024-02-04 **/ #ifndef EXAGEOSTAT_CPP_CONFIGURATIONS_HPP @@ -59,394 +59,378 @@ type Get##name() return std::any_cast(mDictionary[dictionary_name]); \ } -namespace exageostat::configurations { +/** + * @class Configurations + * @brief Contains methods to set and get. + * + */ +class Configurations { +public: + /** - * @class Configurations - * @brief Contains methods to set and get. + * @brief Constructor initializing a Configuration object with default values. * */ - class Configurations { - public: - - /** - * @brief Constructor initializing a Configuration object with default values. - * - */ - Configurations(); - - /** - * @brief destructor to allow calls to the correct concrete destructor. - * - */ - ~Configurations(); - - /** - * @brief Initialize the module arguments. - * @param[in] aArgC The number of arguments being passed into the program from the command line. - * @param[in] apArgV The array of arguments. - * @details This method initializes the command line arguments and set default values for unused args. - * - */ - void InitializeArguments(const int &aArgC, char **apArgV); - - /** - * @brief Initialize the all theta arguments. - * @return void - */ - void InitializeAllTheta(); - - /** - * @brief Initialize data generation arguments.. - * @return void - * - */ - void InitializeDataGenerationArguments(); - - /** - * @brief Initialize data Modeling arguments. - * @return void - * - */ - void InitializeDataModelingArguments(); - - /** - * @brief Initialize data Prediction arguments. - * @return void - * - */ - void InitializeDataPredictionArguments(); + Configurations(); - /** - * @brief Print the usage and accepted Arguments. - * @return void - * - */ - static void PrintUsage(); - - /** START OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ + /** + * @brief destructor to allow calls to the correct concrete destructor. + * + */ + ~Configurations(); - CREATE_SETTER_FUNCTION(ProblemSize, int, aProblemSize, "ProblemSize") + /** + * @brief Initialize the module arguments. + * @param[in] aArgC The number of arguments being passed into the program from the command line. + * @param[in] apArgV The array of arguments. + * @param[in] aEnableR check if R is enabled + * @details This method initializes the command line arguments and set default values for unused args. + * + */ + void InitializeArguments(const int &aArgC, char **apArgV, const bool &aEnableR = false); - CREATE_GETTER_FUNCTION(ProblemSize, int, "ProblemSize") + /** + * @brief Initialize the module arguments. + * @param[in] aArgC The number of arguments being passed into the program from the command line. + * @param[in] apArgV The array of arguments. + * @details This method initializes the command line arguments and set default values for unused args. + * + */ - CREATE_SETTER_FUNCTION(KernelName, const std::string&, aKernel, "Kernel") + /** + * @brief Initialize the all theta arguments. + * @return void + */ + void InitializeAllTheta(); - CREATE_GETTER_FUNCTION(KernelName, const std::string&, "Kernel") + /** + * @brief Initialize data generation arguments.. + * @return void + * + */ + void InitializeDataGenerationArguments(); - CREATE_SETTER_FUNCTION(PGrid, int, aPGrid, "PGrid") + /** + * @brief Initialize data Modeling arguments. + * @return void + * + */ + void InitializeDataModelingArguments(); - CREATE_GETTER_FUNCTION(PGrid, int, "PGrid") + /** + * @brief Initialize data Prediction arguments. + * @return void + * + */ + void InitializeDataPredictionArguments(); - CREATE_SETTER_FUNCTION(QGrid, int, aQGrid, "QGrid") + /** + * @brief Print the usage and accepted Arguments. + * @return void + * + */ + static void PrintUsage(); - CREATE_GETTER_FUNCTION(QGrid, int, "QGrid") + /** START OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ - CREATE_SETTER_FUNCTION(TimeSlot, int, aTimeSlot, "TimeSlot") + CREATE_SETTER_FUNCTION(ProblemSize, int, aProblemSize, "ProblemSize") - CREATE_GETTER_FUNCTION(TimeSlot, int, "TimeSlot") + CREATE_GETTER_FUNCTION(ProblemSize, int, "ProblemSize") - CREATE_SETTER_FUNCTION(Computation, common::Computation, aComputation, "Computation") + CREATE_SETTER_FUNCTION(KernelName, const std::string&, aKernel, "Kernel") - CREATE_GETTER_FUNCTION(Computation, common::Computation, "Computation") + CREATE_GETTER_FUNCTION(KernelName, const std::string&, "Kernel") - CREATE_SETTER_FUNCTION(Precision, common::Precision, aPrecision, "Precision") + CREATE_SETTER_FUNCTION(PGrid, int, aPGrid, "PGrid") - CREATE_GETTER_FUNCTION(Precision, common::Precision, "Precision") + CREATE_GETTER_FUNCTION(PGrid, int, "PGrid") - CREATE_SETTER_FUNCTION(CoresNumber, int, aCoresNumbers, "CoresNumbers") + CREATE_SETTER_FUNCTION(QGrid, int, aQGrid, "QGrid") - CREATE_GETTER_FUNCTION(CoresNumber, int, "CoresNumbers") + CREATE_GETTER_FUNCTION(QGrid, int, "QGrid") - CREATE_SETTER_FUNCTION(GPUsNumbers, int, aGPUsNumber, "GPUsNumbers") + CREATE_SETTER_FUNCTION(TimeSlot, int, aTimeSlot, "TimeSlot") - CREATE_GETTER_FUNCTION(GPUsNumbers, int, "GPUsNumbers") + CREATE_GETTER_FUNCTION(TimeSlot, int, "TimeSlot") - CREATE_SETTER_FUNCTION(DenseTileSize, int, aTileSize, "DTS") + CREATE_SETTER_FUNCTION(Computation, exageostat::common::Computation, aComputation, "Computation") - CREATE_GETTER_FUNCTION(DenseTileSize, int, "DTS") + CREATE_GETTER_FUNCTION(Computation, exageostat::common::Computation, "Computation") - CREATE_SETTER_FUNCTION(LowTileSize, int, aTileSize, "LTS") + CREATE_SETTER_FUNCTION(Precision, exageostat::common::Precision, aPrecision, "Precision") - CREATE_GETTER_FUNCTION(LowTileSize, int, "LTS") + CREATE_GETTER_FUNCTION(Precision, exageostat::common::Precision, "Precision") - CREATE_SETTER_FUNCTION(Band, int, aBand, "Band") + CREATE_SETTER_FUNCTION(CoresNumber, int, aCoresNumbers, "CoresNumbers") - CREATE_GETTER_FUNCTION(Band, int, "Band") + CREATE_GETTER_FUNCTION(CoresNumber, int, "CoresNumbers") - CREATE_SETTER_FUNCTION(MaxRank, int, aMaxRank, "MaxRank") + CREATE_SETTER_FUNCTION(GPUsNumbers, int, aGPUsNumber, "GPUsNumbers") - CREATE_GETTER_FUNCTION(MaxRank, int, "MaxRank") + CREATE_GETTER_FUNCTION(GPUsNumbers, int, "GPUsNumbers") - CREATE_SETTER_FUNCTION(ActualObservationsFilePath, const std::string &, aActualObservationsFilePath, - "ActualObservationsFilePath") + CREATE_SETTER_FUNCTION(DenseTileSize, int, aTileSize, "DTS") - CREATE_GETTER_FUNCTION(ActualObservationsFilePath, std::string, "ActualObservationsFilePath") + CREATE_GETTER_FUNCTION(DenseTileSize, int, "DTS") - CREATE_SETTER_FUNCTION(Seed, int, aSeed, "Seed") + CREATE_SETTER_FUNCTION(LowTileSize, int, aTileSize, "LTS") - CREATE_GETTER_FUNCTION(Seed, int, "Seed") + CREATE_GETTER_FUNCTION(LowTileSize, int, "LTS") - CREATE_SETTER_FUNCTION(LoggerPath, const std::string&, aLoggerPath, "LoggerPath") + CREATE_SETTER_FUNCTION(Band, int, aBand, "Band") - CREATE_GETTER_FUNCTION(LoggerPath, std::string, "LoggerPath") + CREATE_GETTER_FUNCTION(Band, int, "Band") - CREATE_SETTER_FUNCTION(InitialTheta, std::vector &, apTheta, "InitialTheta") + CREATE_SETTER_FUNCTION(MaxRank, int, aMaxRank, "MaxRank") - CREATE_GETTER_FUNCTION(InitialTheta, std::vector &, "InitialTheta") + CREATE_GETTER_FUNCTION(MaxRank, int, "MaxRank") - CREATE_SETTER_FUNCTION(IsOOC, bool, aIsOOC, "OOC") + CREATE_SETTER_FUNCTION(ActualObservationsFilePath, const std::string &, aActualObservationsFilePath, + "ActualObservationsFilePath") - CREATE_GETTER_FUNCTION(IsOOC, bool, "OOC") + CREATE_GETTER_FUNCTION(ActualObservationsFilePath, std::string, "ActualObservationsFilePath") - CREATE_SETTER_FUNCTION(ApproximationMode, int, aApproximationMode, "ApproximationMode") + CREATE_SETTER_FUNCTION(Seed, int, aSeed, "Seed") - CREATE_GETTER_FUNCTION(ApproximationMode, int, "ApproximationMode") + CREATE_GETTER_FUNCTION(Seed, int, "Seed") - CREATE_SETTER_FUNCTION(Logger, bool, aLogger, "Logger") + CREATE_SETTER_FUNCTION(LoggerPath, const std::string&, aLoggerPath, "LoggerPath") - CREATE_GETTER_FUNCTION(Logger, bool, "Logger") + CREATE_GETTER_FUNCTION(LoggerPath, std::string, "LoggerPath") - CREATE_SETTER_FUNCTION(MeanSquareError, double, aMeanSquareError, "MeanSquareError") + CREATE_SETTER_FUNCTION(InitialTheta, std::vector &, apTheta, "InitialTheta") - CREATE_GETTER_FUNCTION(MeanSquareError, double, "MeanSquareError") + CREATE_GETTER_FUNCTION(InitialTheta, std::vector &, "InitialTheta") - CREATE_SETTER_FUNCTION(LowerBounds, std::vector &, apTheta, "LowerBounds") + CREATE_SETTER_FUNCTION(IsOOC, bool, aIsOOC, "OOC") - CREATE_GETTER_FUNCTION(LowerBounds, std::vector &, "LowerBounds") + CREATE_GETTER_FUNCTION(IsOOC, bool, "OOC") - CREATE_SETTER_FUNCTION(UpperBounds, std::vector &, apTheta, "UpperBounds") + CREATE_SETTER_FUNCTION(ApproximationMode, int, aApproximationMode, "ApproximationMode") - CREATE_GETTER_FUNCTION(UpperBounds, std::vector &, "UpperBounds") + CREATE_GETTER_FUNCTION(ApproximationMode, int, "ApproximationMode") - CREATE_SETTER_FUNCTION(EstimatedTheta, std::vector &, apTheta, "EstimatedTheta") + CREATE_SETTER_FUNCTION(Logger, bool, aLogger, "Logger") - CREATE_GETTER_FUNCTION(EstimatedTheta, std::vector &, "EstimatedTheta") + CREATE_GETTER_FUNCTION(Logger, bool, "Logger") - CREATE_SETTER_FUNCTION(StartingTheta, std::vector &, apTheta, "StartingTheta") + CREATE_SETTER_FUNCTION(LowerBounds, std::vector &, apTheta, "LowerBounds") - CREATE_GETTER_FUNCTION(StartingTheta, std::vector &, "StartingTheta") + CREATE_GETTER_FUNCTION(LowerBounds, std::vector &, "LowerBounds") - CREATE_SETTER_FUNCTION(IsNonGaussian, bool, aIsNonGaussian, "IsNonGaussian") + CREATE_SETTER_FUNCTION(UpperBounds, std::vector &, apTheta, "UpperBounds") - CREATE_GETTER_FUNCTION(IsNonGaussian, bool, "IsNonGaussian") + CREATE_GETTER_FUNCTION(UpperBounds, std::vector &, "UpperBounds") - /** - * @brief Getter for the verbosity. - * @return The verbosity mode. - * - */ - static exageostat::common::Verbose GetVerbosity(); + CREATE_SETTER_FUNCTION(EstimatedTheta, std::vector &, apTheta, "EstimatedTheta") - static void SetVerbosity(const common::Verbose &aVerbose); + CREATE_GETTER_FUNCTION(EstimatedTheta, std::vector &, "EstimatedTheta") - /** END OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ - /** START OF THE DATA GENERATION MODULES. **/ + CREATE_SETTER_FUNCTION(StartingTheta, std::vector &, apTheta, "StartingTheta") - CREATE_SETTER_FUNCTION(Dimension, exageostat::common::Dimension, aDimension, "Dimension") + CREATE_GETTER_FUNCTION(StartingTheta, std::vector &, "StartingTheta") - CREATE_GETTER_FUNCTION(Dimension, exageostat::common::Dimension, "Dimension") + CREATE_SETTER_FUNCTION(IsNonGaussian, bool, aIsNonGaussian, "IsNonGaussian") - CREATE_SETTER_FUNCTION(IsSynthetic, bool, aIsSynthetic, "IsSynthetic") + CREATE_GETTER_FUNCTION(IsNonGaussian, bool, "IsNonGaussian") - CREATE_GETTER_FUNCTION(IsSynthetic, bool, "IsSynthetic") + /** + * @brief Getter for the verbosity. + * @return The verbosity mode. + * + */ + static exageostat::common::Verbose GetVerbosity(); - CREATE_SETTER_FUNCTION(IsCSV, bool, aIsCSV, "IsCSV") + static void SetVerbosity(const exageostat::common::Verbose &aVerbose); - CREATE_GETTER_FUNCTION(IsCSV, bool, "IsCSV") + /** END OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ + /** START OF THE DATA GENERATION MODULES. **/ - CREATE_SETTER_FUNCTION(DataPath, const std::string&, aDataPath, "DataPath") + CREATE_SETTER_FUNCTION(Dimension, exageostat::common::Dimension, aDimension, "Dimension") - CREATE_GETTER_FUNCTION(DataPath, std::string, "DataPath") + CREATE_GETTER_FUNCTION(Dimension, exageostat::common::Dimension, "Dimension") - /** END OF THE DATA GENERATION MODULES. **/ - /** START OF THE DATA MODELING MODULES. **/ + CREATE_SETTER_FUNCTION(IsSynthetic, bool, aIsSynthetic, "IsSynthetic") - CREATE_SETTER_FUNCTION(RecoveryFile, const std::string&, aRecoveryFile, "RecoveryFile") + CREATE_GETTER_FUNCTION(IsSynthetic, bool, "IsSynthetic") - CREATE_GETTER_FUNCTION(RecoveryFile, std::string, "RecoveryFile") + CREATE_SETTER_FUNCTION(DataPath, const std::string&, aDataPath, "DataPath") - CREATE_SETTER_FUNCTION(FileLogPath, FILE *, apFileLogPath, "FileLogPath") + CREATE_GETTER_FUNCTION(DataPath, std::string, "DataPath") - CREATE_GETTER_FUNCTION(FileLogPath, FILE *, "FileLogPath") + /** END OF THE DATA GENERATION MODULES. **/ + /** START OF THE DATA MODELING MODULES. **/ - CREATE_SETTER_FUNCTION(FileLogName, const std::string&, aFileLogName, "FileLogName") + CREATE_SETTER_FUNCTION(RecoveryFile, const std::string&, aRecoveryFile, "RecoveryFile") - CREATE_GETTER_FUNCTION(FileLogName, std::string, "FileLogName") + CREATE_GETTER_FUNCTION(RecoveryFile, std::string, "RecoveryFile") - CREATE_SETTER_FUNCTION(AvgExecutedTimePerIteration, double, aAvgExecTimePerIter, "AvgExecuted") + CREATE_SETTER_FUNCTION(FileLogPath, FILE *, apFileLogPath, "FileLogPath") - CREATE_GETTER_FUNCTION(AvgExecutedTimePerIteration, double, "AvgExecuted") + CREATE_GETTER_FUNCTION(FileLogPath, FILE *, "FileLogPath") - CREATE_SETTER_FUNCTION(AvgFlopsPerIteration, double, aAvgFlopsPerIter, "AvgFlops") + CREATE_SETTER_FUNCTION(FileLogName, const std::string&, aFileLogName, "FileLogName") - CREATE_GETTER_FUNCTION(AvgFlopsPerIteration, double, "AvgFlops") + CREATE_GETTER_FUNCTION(FileLogName, std::string, "FileLogName") - CREATE_SETTER_FUNCTION(DistanceMetric, common::DistanceMetric, aDistanceMetric, "DistanceMetric") + CREATE_SETTER_FUNCTION(DistanceMetric, exageostat::common::DistanceMetric, aDistanceMetric, "DistanceMetric") - CREATE_GETTER_FUNCTION(DistanceMetric, common::DistanceMetric, "DistanceMetric") + CREATE_GETTER_FUNCTION(DistanceMetric, exageostat::common::DistanceMetric, "DistanceMetric") - CREATE_SETTER_FUNCTION(MaxMleIterations, int, aMaxMleIterations, "MaxMleIterations") + CREATE_SETTER_FUNCTION(MaxMleIterations, int, aMaxMleIterations, "MaxMleIterations") - CREATE_GETTER_FUNCTION(MaxMleIterations, int, "MaxMleIterations") + CREATE_GETTER_FUNCTION(MaxMleIterations, int, "MaxMleIterations") - CREATE_SETTER_FUNCTION(Accuracy, int, aAccuracy, "Accuracy") + CREATE_SETTER_FUNCTION(Accuracy, int, aAccuracy, "Accuracy") - CREATE_GETTER_FUNCTION(Accuracy, int, "Accuracy") + CREATE_GETTER_FUNCTION(Accuracy, int, "Accuracy") - CREATE_SETTER_FUNCTION(Tolerance, double, aTolerance, "Tolerance") + CREATE_SETTER_FUNCTION(Tolerance, double, aTolerance, "Tolerance") - CREATE_GETTER_FUNCTION(Tolerance, double, "Tolerance") + CREATE_GETTER_FUNCTION(Tolerance, double, "Tolerance") - /** END OF THE DATA MODELING MODULES. **/ - /** START OF THE DATA PREDICTION MODULES. **/ + /** END OF THE DATA MODELING MODULES. **/ + /** START OF THE DATA PREDICTION MODULES. **/ - CREATE_SETTER_FUNCTION(UnknownObservationsNb, int, aUnknownObservationsNumber, "UnknownObservationsNb") + CREATE_SETTER_FUNCTION(UnknownObservationsNb, int, aUnknownObservationsNumber, "UnknownObservationsNb") - CREATE_GETTER_FUNCTION(UnknownObservationsNb, int, "UnknownObservationsNb") + CREATE_GETTER_FUNCTION(UnknownObservationsNb, int, "UnknownObservationsNb") - CREATE_SETTER_FUNCTION(IsMSPE, bool, aIsMSPE, "IsMSPE") + CREATE_SETTER_FUNCTION(IsMSPE, bool, aIsMSPE, "IsMSPE") - CREATE_GETTER_FUNCTION(IsMSPE, bool, "IsMSPE") + CREATE_GETTER_FUNCTION(IsMSPE, bool, "IsMSPE") - CREATE_SETTER_FUNCTION(IsIDW, bool, aIsIDW, "IsIDW") + CREATE_SETTER_FUNCTION(IsIDW, bool, aIsIDW, "IsIDW") - CREATE_GETTER_FUNCTION(IsIDW, bool, "IsIDW") + CREATE_GETTER_FUNCTION(IsIDW, bool, "IsIDW") - CREATE_SETTER_FUNCTION(IsMLOEMMOM, bool, aIsMLOEMMOM, "IsMLOEMMOM") + CREATE_SETTER_FUNCTION(IsMLOEMMOM, bool, aIsMLOEMMOM, "IsMLOEMMOM") - CREATE_GETTER_FUNCTION(IsMLOEMMOM, bool, "IsMLOEMMOM") + CREATE_GETTER_FUNCTION(IsMLOEMMOM, bool, "IsMLOEMMOM") - CREATE_SETTER_FUNCTION(IsFisher, bool, aIsFisher, "IsFisher") + CREATE_SETTER_FUNCTION(IsFisher, bool, aIsFisher, "IsFisher") - CREATE_GETTER_FUNCTION(IsFisher, bool, "IsFisher") + CREATE_GETTER_FUNCTION(IsFisher, bool, "IsFisher") - /** END OF THE DATA PREDICTION MODULES. **/ + /** END OF THE DATA PREDICTION MODULES. **/ - /** - * @brief Check if input value is numerical. - * @param[in] aValue The input from the user side. - * @return The int casted value. - * - */ - static int CheckNumericalValue(const std::string &aValue); + /** + * @brief Check if input value is numerical. + * @param[in] aValue The input from the user side. + * @return The int casted value. + * + */ + static int CheckNumericalValue(const std::string &aValue); - /** - * @brief Checks the value of the dimension parameter. - * @param[in] aDimension A string representing the dimension. - * @return The corresponding dimension value. - * - */ - static exageostat::common::Dimension CheckDimensionValue(const std::string &aDimension); + /** + * @brief Checks the value of the dimension parameter. + * @param[in] aDimension A string representing the dimension. + * @return The corresponding dimension value. + * + */ + static exageostat::common::Dimension CheckDimensionValue(const std::string &aDimension); - /** - * @brief Checks if the kernel value is valid. - * @param[in] aKernel The kernel to check. - * @return void - * - */ - void CheckKernelValue(const std::string &aKernel); + /** + * @brief Checks if the kernel value is valid. + * @param[in] aKernel The kernel to check. + * @return void + * + */ + void CheckKernelValue(const std::string &aKernel); - /** - * @brief Check input computation value. - * @param[in] aValue The input from the user side. - * @return Enum with the selected computation, Error if not exist. - * - */ - static common::Computation CheckComputationValue(const std::string &aValue); + /** + * @brief Check input computation value. + * @param[in] aValue The input from the user side. + * @return Enum with the selected computation, Error if not exist. + * + */ + static exageostat::common::Computation CheckComputationValue(const std::string &aValue); - /** - * @brief Check input precision value. - * @param[in] aValue The input from the user side. - * @return Enum with the selected Precision, Error if not exist. - * - */ - static common::Precision CheckPrecisionValue(const std::string &aValue); + /** + * @brief Check input precision value. + * @param[in] aValue The input from the user side. + * @return Enum with the selected Precision, Error if not exist. + * + */ + static exageostat::common::Precision CheckPrecisionValue(const std::string &aValue); - /** - * @brief Checks the value of the unknown observations parameter. - * @param[in] aValue A string representing the number of unknown observations. - * @return The corresponding integer value. - */ - int CheckUnknownObservationsValue(const std::string &aValue); + /** + * @brief Checks the value of the unknown observations parameter. + * @param[in] aValue A string representing the number of unknown observations. + * @return The corresponding integer value. + */ + int CheckUnknownObservationsValue(const std::string &aValue); - /** - * @brief Initialize a vector with a given size to contain zeros. - * @param[in, out] aTheta A reference to the vector to initialize. - * @param[in] aSize The size of the vector to initialize. - * @return void. - * - */ - static void InitTheta(std::vector &aTheta, const int &aSize); + /** + * @brief Initialize a vector with a given size to contain zeros. + * @param[in, out] aTheta A reference to the vector to initialize. + * @param[in] aSize The size of the vector to initialize. + * @return void. + * + */ + static void InitTheta(std::vector &aTheta, const int &aSize); /** * @brief print the summary of MLE inputs. + * @param[in] aRank A MPI Rank variable * @return void */ inline void PrintSummary(int aRank = 0); - /** - * @brief Calculates the number of observed measurements. - * @return number of observed measurements. - */ - int CalculateZObsNumber(); - + /** + * @brief Calculates the number of observed measurements. + * @return number of observed measurements. + */ + int CalculateZObsNumber(); - private: - /** - * @brief Checks the run mode and sets the verbosity level. - * @param[in] aVerbosity A string representing the desired run mode ("verbose" or "standard"). - * @throws std::range_error if the input string is not "verbose" or "standard". - * @return void - * - */ - static void ParseVerbose(const std::string &aVerbosity); +private: - /** - * @brief Checks if a given string is in camel case format. - * @param[in] aString The string to check. - * @return true if the string is in camel case format, false otherwise. - * - */ - static bool IsCamelCase(const std::string &aString); + /** + * @brief Checks the run mode and sets the verbosity level. + * @param[in] aVerbosity A string representing the desired run mode ("verbose" or "standard"). + * @throws std::range_error if the input string is not "verbose" or "standard". + * @return void + * + */ + static void ParseVerbose(const std::string &aVerbosity); - /** - * @brief Parses a string of theta values and returns an array of doubles. - * @param[in] aInputValues The input string of theta values. - * @return A vector of parsed theta values. - * - */ - static std::vector ParseTheta(const std::string &aInputValues); + /** + * @brief Checks if a given string is in camel case format. + * @param[in] aString The string to check. + * @return true if the string is in camel case format, false otherwise. + * + */ + static bool IsCamelCase(const std::string &aString); - /** - * @brief parse user's input to distance metric. - * @param[in] aDistanceMetric string specifying the used distance metric. - * @return void - */ - void ParseDistanceMetric(const std::string &aDistanceMetric); + /** + * @brief Parses a string of theta values and returns an array of doubles. + * @param[in] aInputValues The input string of theta values. + * @return A vector of parsed theta values. + * + */ + static std::vector ParseTheta(const std::string &aInputValues); - /** - * @brief Initializes the log file. - * @details This function attempts to open a log file with the name returned by GetFileLogName(), - * and sets the file log path accordingly. If an exception occurs during the file opening, - * a default log file named "log_file" is created. - * @return void - */ - void InitLog(); - - /// Used Dictionary - std::unordered_map mDictionary; - /// Used Argument counter - int mArgC = 0; - /// Used Argument vectors - char **mpArgV = nullptr; - //// Used run mode - static exageostat::common::Verbose mVerbosity; - //// Used bool for init theta - static bool mIsThetaInit; - - }; -}//namespace exageostat + /** + * @brief parse user's input to distance metric. + * @param[in] aDistanceMetric string specifying the used distance metric. + * @return void + */ + void ParseDistanceMetric(const std::string &aDistanceMetric); + + /// Used Dictionary + std::unordered_map mDictionary; + /// Used Argument counter + int mArgC = 0; + /// Used Argument vectors + char **mpArgV = nullptr; + //// Used run mode + static exageostat::common::Verbose mVerbosity; + //// Used bool for init theta + static bool mIsThetaInit; + //// Used bool for R allocated memory on heap + static bool mHeapAllocated; +}; #endif //EXAGEOSTAT_CPP_CONFIGURATIONS_HPP diff --git a/inst/include/data-generators/DataGenerator.hpp b/inst/include/data-generators/DataGenerator.hpp index 14cdf110..d040caa7 100644 --- a/inst/include/data-generators/DataGenerator.hpp +++ b/inst/include/data-generators/DataGenerator.hpp @@ -6,7 +6,7 @@ /** * @file DataGenerator.hpp * @brief Contains definition for abstract Data Generator Class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-02-14 **/ @@ -39,9 +39,9 @@ namespace exageostat::generators { * @return unique Pointer to a populated data. * */ - virtual std::unique_ptr> - CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, + virtual std::unique_ptr> + CreateData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) = 0; /** @@ -52,7 +52,7 @@ namespace exageostat::generators { * */ static std::unique_ptr - CreateGenerator(exageostat::configurations::Configurations &aConfigurations); + CreateGenerator(Configurations &aConfigurations); /** * @brief Destructor for the data generator object. diff --git a/inst/include/data-generators/LocationGenerator.hpp b/inst/include/data-generators/LocationGenerator.hpp index a041e1e6..023b6ba4 100644 --- a/inst/include/data-generators/LocationGenerator.hpp +++ b/inst/include/data-generators/LocationGenerator.hpp @@ -6,7 +6,7 @@ /** * @file LocationGenerator.hpp * @brief Generates and manages spatial locations for ExaGeoStat. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-02-04 **/ @@ -14,7 +14,6 @@ #ifndef EXAGEOSTATCPP_LOCATIONGENERATOR_HPP #define EXAGEOSTATCPP_LOCATIONGENERATOR_HPP -#include #include namespace exageostat::generators { diff --git a/inst/include/data-generators/concrete/SyntheticGenerator.hpp b/inst/include/data-generators/concrete/SyntheticGenerator.hpp index fc9b507d..62f86b4c 100644 --- a/inst/include/data-generators/concrete/SyntheticGenerator.hpp +++ b/inst/include/data-generators/concrete/SyntheticGenerator.hpp @@ -6,7 +6,7 @@ /** * @file SyntheticGenerator.hpp * @brief A class for generating synthetic data. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-14 @@ -43,9 +43,9 @@ namespace exageostat::generators::synthetic { * @copydoc DataGenerator::CreateData() * */ - std::unique_ptr> - CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, + std::unique_ptr> + CreateData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) override; /** diff --git a/inst/include/data-loader/DataLoader.hpp b/inst/include/data-loader/DataLoader.hpp index d9bd5d26..b14e5371 100644 --- a/inst/include/data-loader/DataLoader.hpp +++ b/inst/include/data-loader/DataLoader.hpp @@ -6,7 +6,7 @@ /** * @file DataLoader.hpp * @brief Manages data loading operations for ExaGeoStat. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2024-02-04 @@ -35,10 +35,9 @@ namespace exageostat::dataLoader { * @copydoc DataGenerator::CreateData() * */ - std::unique_ptr> - CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, - exageostat::kernels::Kernel &aKernel) override; + std::unique_ptr> + CreateData(Configurations &aConfigurations, const ExaGeoStatHardware &aHardware, + kernels::Kernel &aKernel) override; /** * @brief Reads data from external sources into ExaGeoStat format. @@ -50,9 +49,8 @@ namespace exageostat::dataLoader { * @param aP Partition index for distributed data loading. */ virtual void - ReadData(exageostat::configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, - std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, - const int &aP) = 0; + ReadData(Configurations &aConfigurations, std::vector &aMeasurementsMatrix, std::vector &aXLocations, + std::vector &aYLocations, std::vector &aZLocations, const int &aP) = 0; }; @@ -62,6 +60,7 @@ namespace exageostat::dataLoader { * */ EXAGEOSTAT_INSTANTIATE_CLASS(DataLoader) + } // namespace exageostat #endif //EXAGEOSTATCPP_DATALOADER_HPP diff --git a/inst/include/data-loader/concrete/CSVLoader.hpp b/inst/include/data-loader/concrete/CSVLoader.hpp index f2411178..56d84334 100644 --- a/inst/include/data-loader/concrete/CSVLoader.hpp +++ b/inst/include/data-loader/concrete/CSVLoader.hpp @@ -6,7 +6,7 @@ /** * @file CSVLoader.hpp * @brief A class for generating synthetic data. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2024-02-04 @@ -41,7 +41,7 @@ namespace exageostat::dataLoader::csv { * @copydoc DataLoader::ReadData() * */ - void ReadData(exageostat::configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, + void ReadData(Configurations &aConfigurations, std::vector &aMeasurementsMatrix, std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, const int &aP) override; diff --git a/inst/include/data-units/DescriptorData.hpp b/inst/include/data-units/DescriptorData.hpp index cb5f549d..7cc64c37 100644 --- a/inst/include/data-units/DescriptorData.hpp +++ b/inst/include/data-units/DescriptorData.hpp @@ -6,7 +6,7 @@ /** * @file DescriptorData.hpp * @brief Contains the definition of the DescriptorData class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-18 diff --git a/inst/include/data-units/ExaGeoStatData.hpp b/inst/include/data-units/ExaGeoStatData.hpp index c35f1ac8..14a02ec6 100644 --- a/inst/include/data-units/ExaGeoStatData.hpp +++ b/inst/include/data-units/ExaGeoStatData.hpp @@ -6,10 +6,10 @@ /** * @file ExaGeoStatData.hpp * @brief Contains the definition of the ExaGeoStatData class. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-07-19 + * @date 2024-02-04 **/ #ifndef EXAGEOSTATCPP_EXAGEOSTATDATA_HPP @@ -20,87 +20,91 @@ #include #include -namespace exageostat::dataunits { +/** + * @Class ExaGeoStatData + * @brief Manages geo-statistical data with functions for location and descriptor manipulation + * @tparam T Data Type: float or double + */ +template +class ExaGeoStatData { + +public: + /** + * @brief Constructor for ExaGeoStatData. + * @param[in] aSize The size of the data. + * @param[in] aDimension The dimension of the data. + */ + ExaGeoStatData(const int &aSize, const exageostat::common::Dimension &aDimension); /** - * @Class ExaGeoStatData - * @brief Manages geo-statistical data with functions for location and descriptor manipulation - * @tparam T Data Type: float or double + * @brief Constructor for ExaGeoStatData. + * @param[in] aSize The size of the data. + * @param[in] aDimension The dimension of the data. */ - template - class ExaGeoStatData { - - public: - /** - * @brief Constructor for ExaGeoStatData. - * @param[in] aSize The size of the data. - * @param[in] aDimension The dimension of the data. - */ - ExaGeoStatData(const int &aSize, const exageostat::common::Dimension &aDimension); - - /** - * @brief Default constructor for ExaGeoStatData. - */ - ExaGeoStatData() = default; - - /** - * @brief Destructor for ExaGeoStatData. - */ - ~ExaGeoStatData(); - - /** - * @brief Get the locations. - * @return Pointer to the Locations object. - */ - Locations *GetLocations(); - - /** - * @brief Set the locations. - * @param[in] aLocation Pointer to the Locations object. - */ - void SetLocations(Locations &aLocation); - - /** - * @brief Get the descriptor data. - * @return Pointer to the DescriptorData object. - */ - DescriptorData *GetDescriptorData(); - - /** - * @brief Setter for the number of performed MLE iterations. - * @param[in] aMleIterations number of performed MLE iterations. - * @return void - */ - void SetMleIterations(const int &aMleIterations); - - /** - * @brief Get the number of performed MLE iterations. - * @return Pointer to the DescriptorData object. - */ - int GetMleIterations(); - - /** - * @brief Calculates Median Locations. - * @param[in] aKernelName Name of the Kernel used. - * @param[out] aLocations Location object to save medianLocations in. - * @return void - */ - void CalculateMedianLocations(const std::string &aKernelName, dataunits::Locations &aLocations); - - private: - //// Used descriptor data. - DescriptorData *mpDescriptorData = nullptr; - //// Used locations data. - Locations *mpLocations = nullptr; - //// Current number of performed MLE iterations. - int mMleIterations = 0; - }; + ExaGeoStatData(const int &aSize, const std::string &aDimension); /** - * @brief Instantiates the ExaGeoStatData class for float and double types. - * @tparam T Data Type: float or double + * @brief Default constructor for ExaGeoStatData. */ - EXAGEOSTAT_INSTANTIATE_CLASS(ExaGeoStatData) -} // namespace exageostat + ExaGeoStatData() = default; + + /** + * @brief Destructor for ExaGeoStatData. + */ + ~ExaGeoStatData(); + + /** + * @brief Get the locations. + * @return Pointer to the Locations object. + */ + exageostat::dataunits::Locations *GetLocations(); + + /** + * @brief Set the locations. + * @param[in] aLocation Pointer to the Locations object. + */ + void SetLocations(exageostat::dataunits::Locations &aLocation); + + /** + * @brief Get the descriptor data. + * @return Pointer to the DescriptorData object. + */ + exageostat::dataunits::DescriptorData *GetDescriptorData(); + + /** + * @brief Setter for the number of performed MLE iterations. + * @param[in] aMleIterations number of performed MLE iterations. + * @return void + */ + void SetMleIterations(const int &aMleIterations); + + /** + * @brief Get the number of performed MLE iterations. + * @return Pointer to the DescriptorData object. + */ + int GetMleIterations(); + + /** + * @brief Calculates Median Locations. + * @param[in] aKernelName Name of the Kernel used. + * @param[out] aLocations Location object to save medianLocations in. + * @return void + */ + void CalculateMedianLocations(const std::string &aKernelName, exageostat::dataunits::Locations &aLocations); + using MyTemplateClassDouble = ExaGeoStatData; + +private: + //// Used descriptor data. + exageostat::dataunits::DescriptorData *mpDescriptorData = nullptr; + //// Used locations data. + exageostat::dataunits::Locations *mpLocations = nullptr; + //// Current number of performed MLE iterations. + int mMleIterations = 0; +}; +/** + * @brief Instantiates the ExaGeoStatData class for float and double types. + * @tparam T Data Type: float or double + */ +EXAGEOSTAT_INSTANTIATE_CLASS(ExaGeoStatData) #endif //EXAGEOSTATCPP_EXAGEOSTATDATA_HPP \ No newline at end of file diff --git a/inst/include/data-units/Locations.hpp b/inst/include/data-units/Locations.hpp index ebfb558b..43519585 100644 --- a/inst/include/data-units/Locations.hpp +++ b/inst/include/data-units/Locations.hpp @@ -6,7 +6,7 @@ /** * @file Locations.hpp * @brief Header file for the Locations class, which contains methods to set and get location data. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-27 diff --git a/inst/include/data-units/ModelingDataHolders.hpp b/inst/include/data-units/ModelingDataHolders.hpp index a6f932ea..0d858dc6 100644 --- a/inst/include/data-units/ModelingDataHolders.hpp +++ b/inst/include/data-units/ModelingDataHolders.hpp @@ -1,7 +1,7 @@ /** * @file ModelingDataHolders.hpp * @brief This file contains the definition of the mModelingData struct, which contains all the data needed for modeling. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-08-24 **/ @@ -18,11 +18,11 @@ namespace exageostat::dataunits { template struct mModelingData { /// ExaGeoStatData object containing needed descriptors, and locations. - std::unique_ptr> *mpData; + std::unique_ptr> *mpData; /// Configurations object containing user input data. - configurations::Configurations *mpConfiguration; + Configurations *mpConfiguration; /// Hardware configuration for the ExaGeoStat solver. - const hardware::ExaGeoStatHardware *mpHardware; + const ExaGeoStatHardware *mpHardware; /// Used Kernel for ExaGeoStat Modeling Data. const kernels::Kernel *mpKernel; @@ -36,8 +36,8 @@ namespace exageostat::dataunits { * @param aHardware The hardware configuration object. * @param aKernel The Kernel object. */ - mModelingData(std::unique_ptr> &aData, configurations::Configurations &aConfiguration, - const hardware::ExaGeoStatHardware &aHardware, T &aMatrix, const kernels::Kernel &aKernel) : + mModelingData(std::unique_ptr> &aData, Configurations &aConfiguration, + const ExaGeoStatHardware &aHardware, T &aMatrix, const kernels::Kernel &aKernel) : mpData(std::move(&aData)), mpConfiguration(&aConfiguration), mpHardware(&aHardware), mpMeasurementsMatrix(&aMatrix), mpKernel(&aKernel) {} }; diff --git a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp index bfc85a6e..48f3de04 100644 --- a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp +++ b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStatDescriptor.hpp * @brief Class for creating matrix descriptors used in CHAMELEON and HiCMA libraries. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-16 diff --git a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp index 0f0f5560..35be4d45 100644 --- a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp @@ -6,7 +6,7 @@ /** * @file ChameleonDescriptor.hpp * @brief Defines the ChameleonDescriptor class for creating matrix descriptors using the CHAMELEON library. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp index d3513252..9a34b820 100644 --- a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp @@ -6,7 +6,7 @@ /** * @file HicmaDescriptor.hpp * @brief Defines the Hicma Descriptor class for creating matrix descriptors using the HICMA library. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index c272c65d..549f7a17 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStatHardware.hpp * @brief Contains the definition of the ExaGeoStatHardware class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2024-01-24 @@ -17,58 +17,75 @@ #include -namespace exageostat::hardware { +/** + * @brief Class representing the hardware configuration for the ExaGeoStat solver. + */ +class ExaGeoStatHardware { +public: /** - * @brief Class representing the hardware configuration for the ExaGeoStat solver. + * @brief Constructor for ExaGeoStatHardware. + * @param[in] aComputation The computation mode for the solver. + * @param[in] aCoreNumber The number of CPU cores to use for the solver. + * @param[in] aGpuNumber The number of GPUs to use for the solver. + * */ - class ExaGeoStatHardware { + explicit ExaGeoStatHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); - public: - /** - * @brief Constructor for ExaGeoStatHardware. - * @param[in] aComputation The computation mode for the solver. - * @param[in] aCoreNumber The number of CPU cores to use for the solver. - * @param[in] aGpuNumber The number of GPUs to use for the solver. - * - */ - ExaGeoStatHardware(const common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); + /** + * @brief Constructor for ExaGeoStatHardware. + * @param[in] aComputation The computation mode for the solver as a string. + * @param[in] aCoreNumber The number of CPU cores to use for the solver. + * @param[in] aGpuNumber The number of GPUs to use for the solver. + */ + explicit ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber); - /** - * @brief Destructor for ExaGeoStatHardware. - */ - ~ExaGeoStatHardware(); + /** + * @brief A Finalize caller for Hardware. + * @return void. + */ + void FinalizeHardware(); - /** - * @brief Get the Chameleon hardware context. - * @return Pointer to the hardware context. - * - */ - [[nodiscard]] static void *GetChameleonContext() ; + /** + * @brief Destructor for ExaGeoStatHardware. + */ + ~ExaGeoStatHardware(); - /** - * @brief Get the Hicma hardware context. - * @return Pointer to the hardware context. - * - */ - [[nodiscard]] static void *GetHicmaContext(); + /** + * @brief Initializes hardware configuration. + * @param[in] aComputation The computation mode for the solver. + * @param[in] aCoreNumber The number of CPU cores to use for the solver. + * @param[in] aGpuNumber The number of GPUs to use for the solver. + */ + static void InitHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); - /** - * @brief Get the hardware context. - * @param[in] aComputation Used computation to decide whether to use Hicma or Chameleon context. - * @return Pointer to the hardware context. - * - */ - [[nodiscard]] static void *GetContext(common::Computation aComputation) ; + /** + * @brief Get the Chameleon hardware context. + * @return Pointer to the hardware context. + * + */ + [[nodiscard]] static void *GetChameleonContext(); + + /** + * @brief Get the HiCMA hardware context. + * @return Pointer to the hardware context. + * + */ + [[nodiscard]] static void *GetHicmaContext(); + + /** + * @brief Get the hardware context. + * @param[in] aComputation Used computation to decide whether to use Hicma or Chameleon context. + * @return Pointer to the hardware context. + * + */ + [[nodiscard]] static void *GetContext(exageostat::common::Computation aComputation); private: //// Used Pointer to the Chameleon hardware context. static void *mpChameleonContext; //// Used Pointer to the Hicma hardware context. static void *mpHicmaContext; - //// Used Computation mode for the solver. - common::Computation mComputation; }; -} // namespace exageostat #endif // EXAGEOSTATCPP_EXAGEOSTATHARDWARE_HPP \ No newline at end of file diff --git a/inst/include/helpers/BasselFunction.hpp b/inst/include/helpers/BasselFunction.hpp index 62430f2a..541a6974 100644 --- a/inst/include/helpers/BasselFunction.hpp +++ b/inst/include/helpers/BasselFunction.hpp @@ -6,7 +6,7 @@ /** * @file BasselFunction.hpp * @brief This file contains the BasselFunction class which provides methods for computing derivatives of the modified Bessel function of the second kind. These functions are crucial in statistical and mathematical computations, especially in fields such as geostatistics and spatial analysis. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-01-24 diff --git a/inst/include/helpers/ByteHandler.hpp b/inst/include/helpers/ByteHandler.hpp index cb071f4c..f8139aa6 100644 --- a/inst/include/helpers/ByteHandler.hpp +++ b/inst/include/helpers/ByteHandler.hpp @@ -6,7 +6,7 @@ /** * @file ByteHandler.hpp * @brief Implementation of byte manipulation functions for ExaGeoStat. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2024-01-24 diff --git a/inst/include/helpers/CommunicatorMPI.hpp b/inst/include/helpers/CommunicatorMPI.hpp index 3c9595eb..23162c58 100644 --- a/inst/include/helpers/CommunicatorMPI.hpp +++ b/inst/include/helpers/CommunicatorMPI.hpp @@ -6,7 +6,7 @@ /** * @file CommunicatorMPI.hpp * @brief Defines the CommunicatorMPI class for MPI rank communication. - * @version 1.0.0 + * @version 1.1.0 * @author Sameh Abdulah * @date 2023-11-10 **/ diff --git a/inst/include/helpers/DiskWriter.hpp b/inst/include/helpers/DiskWriter.hpp index 4e4a0f12..7d43d834 100644 --- a/inst/include/helpers/DiskWriter.hpp +++ b/inst/include/helpers/DiskWriter.hpp @@ -6,10 +6,10 @@ /** * @file DiskWriter.hpp * @brief Contains the definition of the DiskWriter class for writing data to disk. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-01-24 + * @date 2024-01-24 **/ #ifndef EXAGEOSTATCPP_DISKWRITER_HPP diff --git a/inst/include/helpers/DistanceCalculationHelpers.hpp b/inst/include/helpers/DistanceCalculationHelpers.hpp index b297be6f..e035995a 100644 --- a/inst/include/helpers/DistanceCalculationHelpers.hpp +++ b/inst/include/helpers/DistanceCalculationHelpers.hpp @@ -6,7 +6,7 @@ /** * @file DistanceCalculationHelpers.hpp * @brief Contains the definition of the DistanceCalculationHelpers class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/inst/include/kernels/Kernel.hpp b/inst/include/kernels/Kernel.hpp index 6fa85fa6..dabb3261 100644 --- a/inst/include/kernels/Kernel.hpp +++ b/inst/include/kernels/Kernel.hpp @@ -7,7 +7,7 @@ /** * @file Kernels.hpp * @brief Header file for the Kernels class, which contains the main kernel functions. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/BivariateMaternFlexible.hpp b/inst/include/kernels/concrete/BivariateMaternFlexible.hpp index c8286aea..e2688090 100644 --- a/inst/include/kernels/concrete/BivariateMaternFlexible.hpp +++ b/inst/include/kernels/concrete/BivariateMaternFlexible.hpp @@ -6,7 +6,7 @@ /** * @file BivariateMaternFlexible.hpp * @brief Defines the BivariateMaternFlexible class, a Bivariate Matern Flexible kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp b/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp index ae702c31..05226389 100644 --- a/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp +++ b/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp @@ -6,7 +6,7 @@ /** * @file BivariateMaternParsimonious.hpp * @brief Defines the BivariateMaternParsimonious class, a Bivariate Matern Parsimonious kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp b/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp index a6b0416a..9a0b8dc0 100644 --- a/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp +++ b/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp @@ -6,7 +6,7 @@ /** * @file BivariateSpacetimeMaternStationary.hpp * @brief Defines the BivariateSpacetimeMaternStationary class, a Bivariate Spacetime Matern Stationary kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp b/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp index c81bd704..4aa525c8 100644 --- a/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp +++ b/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp @@ -6,7 +6,7 @@ /** * @file TrivariateMaternParsimonious.hpp * @brief Defines the TrivariateMaternParsimonious class, a Trivariate Matern Parsimonious kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp b/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp index 3248cfed..3afaf163 100644 --- a/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp +++ b/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateExpNonGaussian.hpp * @brief Defines the UnivariateExpNonGaussian class, a Univariate Exp Non Gaussian kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp index b1a6a95f..3866b690 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDbeta.hpp * @brief Defines the UnivariateMaternDbeta class, a Univariate Matern Dbeta kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp index a012eb87..6ac9e051 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdbetaBeta.hpp * @brief Defines the UnivariateMaternDdbetaBeta class, a Univariate Matern Ddbeta Beta kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp index 689e37ee..ae53d4c6 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdbetaNu.hpp * @brief Defines the UnivariateMaternDdbetaNu class, a Univariate Matern Ddbeta Nu kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp index 5623debd..29678d14 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdnuNu.hpp * @brief Defines the UnivariateMaternDdnuNu class, a Univariate Matern Ddnu Nu kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp index 162bee37..619705bc 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquare.hpp * @brief Defines the UnivariateMaternDdsigmaSquare class, a univariate stationary Matern kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp index 57f622fc..37606bf7 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquareBeta.hpp * @brief Defines the UnivariateMaternDdsigmaSquareBeta class, a Univariate Matern Ddsigma Square Beta kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp index ae33506a..c443b001 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquareNu.hpp * @brief Defines the UnivariateMaternDdsigmaSquareNu class, a Univariate Matern Ddsigma Square Nu kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDnu.hpp b/inst/include/kernels/concrete/UnivariateMaternDnu.hpp index f9eefb5a..d10ba4e0 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDnu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDnu.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDnu.hpp * @brief Defines the UnivariateMaternDnu class, a Univariate Matern Dnu kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp b/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp index 1da69311..eade1268 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDsigmaSquare.hpp * @brief Defines the UnivariateMaternDsigmaSquare class, a Univariate Matern Dsigma Square kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp b/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp index 1ee3a74e..b2e638af 100644 --- a/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternNonGaussian.hpp * @brief Defines the UnivariateMaternNonGaussian class, a Univariate Matern Non Gaussian kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp index 86e6cae9..52febd31 100644 --- a/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternNuggetsStationary.hpp * @brief Defines the UnivariateMaternNuggetsStationary class, a Univariate Matern Nuggets Stationary kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateMaternStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternStationary.hpp index ceb8cc7c..18040d86 100644 --- a/inst/include/kernels/concrete/UnivariateMaternStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternStationary.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternStationary.hpp * @brief Defines the UnivariateMaternStationary class, a univariate stationary Matern kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp b/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp index 35b52842..6ea8d77a 100644 --- a/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp @@ -6,7 +6,7 @@ /** * @file UnivariateSpacetimeMaternStationary.hpp * @brief Defines the UnivariateSpacetimeMaternStationary class, a Univariate Spacetime Matern Stationary kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp index 68bf6729..cc42108f 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp @@ -6,7 +6,7 @@ /** * @file LinearAlgebraFactory.hpp * @brief Header file for the LinearAlgebraFactory class, which creates linear algebra solvers based on the input computation type. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-03-20 **/ diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp index 150bc616..85d57aad 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp @@ -6,7 +6,7 @@ /** * @file LinearAlgebraMethods.hpp * @brief Header file for the LinearAlgebraMethods class, which defines the interface for linear algebra solvers. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 @@ -25,7 +25,7 @@ extern "C" { #include } -#include +#include #include #include #include @@ -59,7 +59,7 @@ namespace exageostat::linearAlgebra { * @return void * */ - void InitiateDescriptors(configurations::Configurations &aConfigurations, + void InitiateDescriptors(Configurations &aConfigurations, dataunits::DescriptorData &aDescriptorData, const int &aP, T *apMeasurementsMatrix = nullptr); @@ -68,7 +68,7 @@ namespace exageostat::linearAlgebra { * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in,out] aDescriptorData Descriptor Data object to be populated with descriptors and data. */ - void InitiateFisherDescriptors(configurations::Configurations &aConfigurations, + void InitiateFisherDescriptors(Configurations &aConfigurations, dataunits::DescriptorData &aDescriptorData); /** @@ -80,8 +80,8 @@ namespace exageostat::linearAlgebra { * @return void * */ - void InitiatePredictionDescriptors(configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, const int &aP); + void InitiatePredictionDescriptors(Configurations &aConfigurations, + std::unique_ptr> &aData, const int &aP); /** * @brief Initializes the descriptors necessary for the Prediction Auxiliary function MLE-MLOE-MMOM. @@ -92,8 +92,8 @@ namespace exageostat::linearAlgebra { * @return void * */ - void InitiateMLOEMMOMDescriptors(configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, const int &aP); + void InitiateMLOEMMOMDescriptors(Configurations &aConfigurations, + std::unique_ptr> &aData, const int &aP); /** * @brief Generates synthetic data. @@ -104,9 +104,9 @@ namespace exageostat::linearAlgebra { * @return None. * */ - void GenerateSyntheticData(configurations::Configurations &aConfigurations, - const hardware::ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, + void GenerateSyntheticData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, const kernels::Kernel &aKernel); /** @@ -152,8 +152,8 @@ namespace exageostat::linearAlgebra { * */ void - GenerateObservationsVector(configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, + GenerateObservationsVector(Configurations &aConfigurations, + std::unique_ptr> &aData, dataunits::Locations *apLocation1, dataunits::Locations *apLocation2, dataunits::Locations *apLocation3, const int &aDistanceMetric, const kernels::Kernel &aKernel); @@ -169,9 +169,9 @@ namespace exageostat::linearAlgebra { * @return log likelihood value * */ - virtual T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, - configurations::Configurations &aConfigurations, const double *apTheta, + virtual T ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, + Configurations &aConfigurations, const double *apTheta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) = 0; /** @@ -332,11 +332,11 @@ namespace exageostat::linearAlgebra { * @param[in] aKernel Reference to the kernel object to use. * @return the prediction Mean Square Error (MSPE). */ - T *ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, + T *ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfiguration, + const ExaGeoStatHardware &aHardware, + Configurations &aConfiguration, exageostat::dataunits::Locations &aMissLocations, exageostat::dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); @@ -357,11 +357,11 @@ namespace exageostat::linearAlgebra { * @param[in] aKernel Reference to the kernel object to use. * @return the prediction Mean Square Error (MSPE). */ - T *ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, T *apTheta, + T *ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfiguration, + const ExaGeoStatHardware &aHardware, + Configurations &aConfiguration, exageostat::dataunits::Locations &aMissLocations, exageostat::dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); @@ -418,7 +418,7 @@ namespace exageostat::linearAlgebra { * @param[in] aDescData Descriptor data containing required Z matrix Descriptor. * @param[in] aP the P value of the kernel multiplied by time slot. */ - void ExaGeoStatGetZObs(exageostat::configurations::Configurations &aConfigurations, T *apZ, const int &aSize, + void ExaGeoStatGetZObs(Configurations &aConfigurations, T *apZ, const int &aSize, exageostat::dataunits::DescriptorData &aDescData, T *apMeasurementsMatrix, const int &aP); /** @@ -435,9 +435,9 @@ namespace exageostat::linearAlgebra { * @param[in] aKernel Reference to the kernel object to use. * @return void */ - void ExaGeoStatMLETileMLOEMMOM(exageostat::configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, - const exageostat::hardware::ExaGeoStatHardware &aHardware, T *apTruthTheta, + void ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigurations, + std::unique_ptr> &aData, + const ExaGeoStatHardware &aHardware, T *apTruthTheta, T *apEstimatedTheta, dataunits::Locations &aMissLocations, dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); @@ -451,9 +451,9 @@ namespace exageostat::linearAlgebra { * @return Fisher Matrix */ T * - ExaGeoStatFisherTile(configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, - const hardware::ExaGeoStatHardware &aHardware, T *apTheta, + ExaGeoStatFisherTile(Configurations &aConfigurations, + std::unique_ptr> &aData, + const ExaGeoStatHardware &aHardware, T *apTheta, const kernels::Kernel &aKernel); /** diff --git a/inst/include/linear-algebra-solvers/concrete/ChameleonHeaders.hpp b/inst/include/linear-algebra-solvers/concrete/ChameleonHeaders.hpp index d851e510..ece6a56a 100644 --- a/inst/include/linear-algebra-solvers/concrete/ChameleonHeaders.hpp +++ b/inst/include/linear-algebra-solvers/concrete/ChameleonHeaders.hpp @@ -1,7 +1,7 @@ /** * @file ChameleonHeaders.hpp * @brief This file contains the necessary includes for using the Chameleon library. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-08-24 **/ diff --git a/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp b/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp index 9d216bbe..2093821b 100644 --- a/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp +++ b/inst/include/linear-algebra-solvers/concrete/HicmaHeaders.hpp @@ -1,7 +1,7 @@ /** * @file HicmaHeaders.hpp * @brief This file contains the necessary includes for using the Chameleon library. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-08-24 **/ diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp index 6c2d5c61..84fd332f 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp @@ -7,7 +7,7 @@ * @file ChameleonImplementation.hpp * @brief This file contains the declaration of ChameleonImplementation class. * @details ChameleonImplementation is a concrete implementation of the LinearAlgebraMethods class for the common functionality implementation shared between dense and diagonal-super tile matrices. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 @@ -33,8 +33,8 @@ namespace exageostat::linearAlgebra { * @brief Calculates the log likelihood value of a given value theta. * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() */ - T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, - configurations::Configurations &aConfigurations, const double *theta, + T ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, + Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) override; /** diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp index b902e8ab..fc373763 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp @@ -7,7 +7,7 @@ * @file ChameleonImplementationDense.hpp * @brief This file contains the declaration of ChameleonImplementationDense class. * @details ChameleonImplementationDense is a concrete implementation of ChameleonImplementation class for dense matrices. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp index 544dbb97..be3ed402 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp @@ -7,7 +7,7 @@ * @file ChameleonImplementationDST.hpp * @brief This file contains the declaration of ChameleonImplementationDST class. * @details ChameleonImplementationDST is a concrete implementation of LinearAlgebraMethods class for diagonal super tile matrices. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-26 diff --git a/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp index 22fdf9bf..dfa3594f 100644 --- a/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp @@ -7,7 +7,7 @@ * @file HicmaImplementation.hpp * @brief This file contains the declaration of HicmaImplementation class. * @details HicmaImplementation is a concrete implementation of LinearAlgebraMethods class for tile low-rank matrices. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-26 @@ -45,16 +45,16 @@ namespace exageostat::linearAlgebra::tileLowRank { * @param[in] aConfigurations Reference to the Configurations object. * @param[in] aP the P value of the kernel multiplied by time slot. */ - void SetModelingDescriptors(std::unique_ptr> &aData, - configurations::Configurations &aConfigurations, const int &aP); + void SetModelingDescriptors(std::unique_ptr> &aData, + Configurations &aConfigurations, const int &aP); /** * @brief Calculates the log likelihood value of a given value theta. * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() */ - T ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &apHardware, - std::unique_ptr> &aData, - configurations::Configurations &aConfigurations, const double *theta, + T ExaGeoStatMLETile(const ExaGeoStatHardware &apHardware, + std::unique_ptr> &aData, + Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) override; /** diff --git a/inst/include/prediction/Prediction.hpp b/inst/include/prediction/Prediction.hpp index 5e7a42fb..d880f125 100644 --- a/inst/include/prediction/Prediction.hpp +++ b/inst/include/prediction/Prediction.hpp @@ -6,7 +6,7 @@ /** * @file Prediction.hpp * @brief Contains the definition of the Prediction class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 @@ -38,9 +38,9 @@ namespace exageostat::prediction { * @param[in] aKernel Reference to the kernel object to use. * @return */ - static void PredictMissingData(const exageostat::hardware::ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, - exageostat::configurations::Configurations &aConfigurations, + static void PredictMissingData(const ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, + Configurations &aConfigurations, T *apMeasurementsMatrix, const kernels::Kernel &aKernel); /** @@ -56,8 +56,8 @@ namespace exageostat::prediction { * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ - static void InitializePredictionArguments(exageostat::configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, + static void InitializePredictionArguments(Configurations &aConfigurations, + std::unique_ptr> &aData, std::unique_ptr> &aLinearAlgebraSolver, T *apZObs, T *apZActual, exageostat::dataunits::Locations &aMissLocation, diff --git a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp index d9ebf857..c39c3d4b 100644 --- a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp +++ b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp @@ -6,7 +6,7 @@ /** * @file PredictionAuxiliaryFunctions.hpp * @brief Contains the definition of the PredictionAuxiliaryFunctions.hpp class. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/inst/include/prediction/PredictionHelpers.hpp b/inst/include/prediction/PredictionHelpers.hpp index 9a684390..c1fdbe02 100644 --- a/inst/include/prediction/PredictionHelpers.hpp +++ b/inst/include/prediction/PredictionHelpers.hpp @@ -6,7 +6,7 @@ /** * @file PredictionHelpers.hpp * @brief Contains the definition of the PredictionHelpers.hpp class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 @@ -41,8 +41,8 @@ namespace exageostat::prediction { * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ - static void PickRandomPoints(exageostat::configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, + static void PickRandomPoints(Configurations &aConfigurations, + std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, exageostat::dataunits::Locations &aMissLocation, exageostat::dataunits::Locations &aObsLocation, const int &aP); diff --git a/inst/include/results/Results.hpp b/inst/include/results/Results.hpp index 8037dd48..08dd54cc 100644 --- a/inst/include/results/Results.hpp +++ b/inst/include/results/Results.hpp @@ -6,7 +6,7 @@ /** * @file Results.hpp * @brief Defines the Results class for storing and accessing result data. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-09-14 **/ diff --git a/inst/include/utilities/EnumStringParser.hpp b/inst/include/utilities/EnumStringParser.hpp new file mode 100644 index 00000000..3f2746a9 --- /dev/null +++ b/inst/include/utilities/EnumStringParser.hpp @@ -0,0 +1,66 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file EnumStringParser.hpp + * @brief Provides utility functions for parsing enumeration values from strings. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-20 +**/ + +#ifndef EXAGEOSTATCPP_ENUMSTRINGPARSER_HPP +#define EXAGEOSTATCPP_ENUMSTRINGPARSER_HPP + +#include + +#include +#include + +using namespace exageostat::common; + +/** + * @brief Convert a string representation of computation mode to its corresponding enum value. + * @param[in] aComputation String representation of computation mode. + * @return Computation enum value. + */ +inline Computation GetInputComputation(std::string aComputation) { + std::transform(aComputation.begin(), aComputation.end(), + aComputation.begin(), ::tolower); + + if (aComputation == "exact" || aComputation == "dense") { + return EXACT_DENSE; + } else if (aComputation == "dst" || aComputation == "diagonal_super_tile") { + return DIAGONAL_APPROX; + } else if (aComputation == "tlr" || aComputation == "tile_low_rank") { + return TILE_LOW_RANK; + } else { + const std::string msg = "Error in Initialization : Unknown computation Value" + std::string(aComputation); + throw API_EXCEPTION(msg, INVALID_ARGUMENT_ERROR); + } +} + +/** + * @brief Converts string to dimension enum. + * @param[in] aDimension Dimension as a string. + * @return Dimension as an enum. + */ +inline Dimension GetInputDimension(std::string aDimension) { + std::transform(aDimension.begin(), aDimension.end(), + aDimension.begin(), ::tolower); + + if (aDimension == "2d") { + return Dimension2D; + } else if (aDimension == "3d") { + return Dimension3D; + } else if (aDimension == "st") { + return DimensionST; + } else { + const std::string msg = "Error in Initialization : Unknown computation Value" + std::string(aDimension); + throw API_EXCEPTION(msg, INVALID_ARGUMENT_ERROR); + } +} + +#endif //EXAGEOSTATCPP_ENUMSTRINGPARSER_HPP diff --git a/inst/include/utilities/ErrorHandler.hpp b/inst/include/utilities/ErrorHandler.hpp new file mode 100644 index 00000000..5d225f0a --- /dev/null +++ b/inst/include/utilities/ErrorHandler.hpp @@ -0,0 +1,131 @@ +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ErrorHandler.hpp + * @version 1.1.0 + * @brief Provides error handling functionalities. + * @details Defines macros and functions for handling errors and warnings. + * @author Mahmoud ElKarargy + * @author David Helmy + * @date 2024-01-20 +**/ + +#ifndef EXAGEOSTATCPP_ERRORHANDLER_HPP +#define EXAGEOSTATCPP_ERRORHANDLER_HPP + +#include + +#ifdef USE_CUDA +#include +#endif + +/** + * @brief EXAGEOSTAT API Exceptions Macro to use for Errors and Warnings. + */ + #define API_EXCEPTION(MESSAGE, ERROR_TYPE) \ + APIException(MESSAGE, ERROR_TYPE) + +#ifdef USE_CUDA +/** + * @brief Useful macro wrapper for all cuda API calls to ensure correct returns, + * and error throwing on failures. + */ +#define GPU_ERROR_CHECK(ans) { APIException::AssertGPU((ans), __FILE__, __LINE__); } +#endif + +/** + * @brief Enumeration for error types. + */ +enum ErrorType : int { + RUNTIME_ERROR = 0, + RANGE_ERROR = 1, + INVALID_ARGUMENT_ERROR = 2, + WARNING = 3, +}; + +/** + * @class APIException + * @brief Custom exception class for handling API errors and warnings. + */ +class APIException : public std::exception{ + +public: + + /** + * @brief Constructor for APIException. + * @param[in] aMessage The error or warning message. + * @param[in] aErrorCode The error type. + */ + APIException(const std::string &aMessage, const ErrorType &aErrorCode) { + + if (aErrorCode != WARNING) { + APIException::ThrowError(aMessage, aErrorCode); + } else { + APIException::ThrowWarning(aMessage); + } + } + + /** + * @brief Destructor for APIException. + */ + ~APIException() override = default; + +#ifdef USE_CUDA + /** + * @brief Function to assert the return code of a CUDA API call and ensure it completed successfully. + * @param[in] aCode The code returned from the CUDA API call. + * @param[in] aFile The name of the file that the assertion was called from. + * @param[in] aLine The line number in the file that the assertion was called from. + */ + inline static void AssertGPU(cudaError_t aCode, const char *aFile, int aLine) + { + if (aCode != cudaSuccess) + { +#ifdef USING_R + std::string s="GPU Assert: "+std::string(cudaGetErrorString(aCode)); + Rcpp::stop(s); +#else + char s[200]; + sprintf((char*)s,"GPU Assert: %s %s %d\n", cudaGetErrorString(aCode), aFile, aLine); + throw std::invalid_argument(s); +#endif + } + } +#endif + + +private: + + /** + * @brief Helper function to throw error based on error type. + * @param[in] aString The error message. + * @param[in] aErrorCode The error type. + */ + static void ThrowError(const std::string &aString, const ErrorType &aErrorCode) { +#ifdef USING_R +// Rcpp::stop(aString); +#else + if (aErrorCode == RUNTIME_ERROR) { + throw std::runtime_error(aString); + } else if (aErrorCode == INVALID_ARGUMENT_ERROR) { + throw std::invalid_argument(aString); + } else if (aErrorCode == RANGE_ERROR) { + throw std::range_error(aString); + } +#endif + } + + /** + * @brief Helper function to throw warning. + * @param[in] aString The warning message. + */ + static void ThrowWarning(const std::string &aString) { +#ifdef USING_R +// Rcpp::warning(aString); +#endif + } +}; + +#endif //EXAGEOSTATCPP_ERRORHANDLER_HPP \ No newline at end of file diff --git a/inst/include/utilities/Logger.hpp b/inst/include/utilities/Logger.hpp new file mode 100644 index 00000000..ae9aacb3 --- /dev/null +++ b/inst/include/utilities/Logger.hpp @@ -0,0 +1,132 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file Logger.hpp + * @brief Provides logging and timing macros for debugging and profiling. + * @details Defines macros for verbose logging, various levels of logging, and timing. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-04 +**/ + +#ifndef EXAGEOSTATCPP_LOGGER_HPP +#define EXAGEOSTATCPP_LOGGER_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * @def DEFAULT_PRECISION + * @brief The value of the default C++ std::cout number of precision. + */ +#define DEFAULT_PRECISION 6 + +/** + * @def VERBOSE(msg) + * @brief Verbose macro for logging and debugging mode. + */ +#define VERBOSE(msg) \ + if(Configurations::GetVerbosity() == exageostat::common::Verbose::DETAILED_MODE && \ + !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) { \ + std::ostringstream oss; \ + oss << "\t\t\t " << msg << std::endl; \ + EXAGEOSTAT_PRINTER(oss.str()); \ + } + +/** + * @def LOGGER_1(msg) + * @brief LOGGER_1 macro for logging outputs with double taps and new line at the end. + */ +#define LOGGER_1(msg) \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + std::ostringstream oss; \ + oss << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg << std::endl; \ + EXAGEOSTAT_PRINTER(oss.str()); \ + } + +/** + * @def LOGGER_2(msg, A) + * @brief LOGGER_2 macro for logging outputs with double taps and without new line at the end. + */ +#define LOGGER_2(msg, A) \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + std::ostringstream oss; \ + oss << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; \ + EXAGEOSTAT_PRINTER(oss.str()); \ +} +/** + * @def LOGGER_CONTROL(x, A, B, FUNC, ...) + * @brief LOGGER_CONTROL is The internal macro that simply strips the excess and ends up with the required macro + */ +#define LOGGER_CONTROL(x, A, B, FUNC, ...) FUNC + +/** + * @def LOGGER(...) + * @brief LOGGER macro that's called, Used to logging outputs. + */ +#define LOGGER(...) LOGGER_CONTROL(,##__VA_ARGS__, \ + LOGGER_2(__VA_ARGS__), \ + LOGGER_1(__VA_ARGS__), \ + ) + +/** + * @def LOGGER_PRECISION_1(msg, precision) + * @brief LOGGER_PRECISION_1 macro for logging outputs without any taps, without new line at the end, and with customized precision. + */ +#define LOGGER_PRECISION_1(msg, precision) \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + std::ostringstream oss; \ + oss << std::fixed << std::setprecision(precision) << msg; \ + EXAGEOSTAT_PRINTER(oss.str()); \ + } + +/** + * @def LOGGER_PRECISION_2(msg) + * @brief LOGGER_PRECISION_2 macro for logging outputs without any taps, without new line at the end, and with default C++ precision. + */ +#define LOGGER_PRECISION_2(msg) \ + if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) {\ + std::ostringstream oss; \ + oss << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; \ + EXAGEOSTAT_PRINTER(oss.str()); \ + } +/** + * @def LOGGER_PRECISION_CONTROL + * @brief is The internal macro that simply strips the excess and ends up with the required macro + */ +#define LOGGER_PRECISION_CONTROL(x, A, B, FUNC, ...) FUNC + +/** + * @def LOGGER_PRECISION(...) + * @brief LOGGER_PRECISION macro that's called, Used for logging outputs with precision. + */ +#define LOGGER_PRECISION(...) LOGGER_PRECISION_CONTROL(,##__VA_ARGS__, \ + LOGGER_PRECISION_1(__VA_ARGS__), \ + LOGGER_PRECISION_2(__VA_ARGS__), \ + ) + +/** + * @def START_TIMING(t) + * @brief Timing macro to start timing. + */ +#define START_TIMING(t) auto t##_start = std::chrono::high_resolution_clock::now() + +/** + * @def STOP_TIMING(t) + * @brief Timing macro to stop timing. + */ +#define STOP_TIMING(t) auto t##_end = std::chrono::high_resolution_clock::now(); \ + t = std::chrono::duration_cast>(t##_end - t##_start).count() + +#endif //EXAGEOSTATCPP_LOGGER_HPP diff --git a/inst/include/utilities/Printer.hpp b/inst/include/utilities/Printer.hpp new file mode 100644 index 00000000..1185e936 --- /dev/null +++ b/inst/include/utilities/Printer.hpp @@ -0,0 +1,38 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file Printer.hpp + * @version 1.1.0 + * @brief Provides printing functionality for output messages. + * @details Defines macros for printing messages to the console or R output. + * @author Mahmoud ElKarargy + * @author David Helmy + * @date 2024-01-20 +**/ + +#ifndef EXAGEOSTATCPP_PRINTER_HPP +#define EXAGEOSTATCPP_PRINTER_HPP + +#include +#ifdef USING_R +//#include +#endif + +/** + * @def EXAGEOSTAT_PRINTER(message) + * @brief Macro for printing messages. + * @details Depending on whether the code is compiled with Rcpp support, it prints the message to the console or R output. + */ +#ifdef USING_R +#define EXAGEOSTAT_PRINTER(message) \ + std::cout <<(message); +// Rcpp::Rcout<<(message); +#else +#define EXAGEOSTAT_PRINTER(message) \ + std::cout<<(message); +#endif + +#endif //EXAGEOSTATCPP_PRINTER_HPP diff --git a/scripts/Benchmarking.sh b/scripts/Benchmarking.sh index 1bd12b0b..a21c9ae3 100644 --- a/scripts/Benchmarking.sh +++ b/scripts/Benchmarking.sh @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file config.sh -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-10-10 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6fde05af..3d5c353f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,34 +5,35 @@ # @file CMakeLists.txt # @brief This file contains the CMake configuration for the ExaGeoStat library. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah -# @date 2023-01-30 +# @date 2024-02-04 # Add subdirectories for configurations, data-generators, data-units, and linear-algebra-solvers. -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/configurations) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-generators) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-units) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/linear-algebra-solvers) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/kernels) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/api) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/helpers) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/hardware) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/prediction) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/results) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-loader) +add_subdirectory(configurations) +add_subdirectory(data-generators) +add_subdirectory(data-units) +add_subdirectory(linear-algebra-solvers) +add_subdirectory(kernels) +add_subdirectory(api) +add_subdirectory(helpers) +add_subdirectory(hardware) +add_subdirectory(prediction) +add_subdirectory(results) +add_subdirectory(data-loader) + +if (USE_R) + add_subdirectory(Rcpp-adapters) +endif () # Set the name of the library to be created. set(LIB_NAME ${PROJECT_NAME}) - # Create the library with the specified source files and linking libraries. -add_library(${LIB_NAME} - STATIC - ${SOURCES} - ) +add_library(${LIB_NAME} SHARED ${SOURCES}) + target_compile_definitions(${LIB_NAME} PUBLIC ${COMPILE_DEFINITIONS}) -target_link_libraries(${LIB_NAME} PUBLIC ${LIBS}) +target_link_libraries(${LIB_NAME} ${LIBS}) # Set the version of the library. set_target_properties(${LIB_NAME} diff --git a/src/Rcpp-adapters/CMakeLists.txt b/src/Rcpp-adapters/CMakeLists.txt new file mode 100644 index 00000000..50006846 --- /dev/null +++ b/src/Rcpp-adapters/CMakeLists.txt @@ -0,0 +1,17 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-01-29 + +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/FunctionsAdapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/RcppExports.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/RcppModules.cpp + ${SOURCES} + PARENT_SCOPE + ) \ No newline at end of file diff --git a/src/Rcpp-adapters/FunctionsAdapter.cpp b/src/Rcpp-adapters/FunctionsAdapter.cpp new file mode 100644 index 00000000..a17c2257 --- /dev/null +++ b/src/Rcpp-adapters/FunctionsAdapter.cpp @@ -0,0 +1,292 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file FunctionsAdapter.cpp + * @brief Header file for function adapters in the ExaGeoStat software. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-01-29 +**/ + +#include + +#include +#include +#include + +using namespace std; +using namespace Rcpp; + +using namespace exageostat::dataunits; +using namespace exageostat::api; +using namespace exageostat::common; + +namespace exageostat::adapters { + + Configurations * + R_InitializeArguments(const int &aProblemSize, const string &aKernelName, const vector &aTileSize, + const vector &aP_QGrid, const int &aTimeSlot, const string &aComputation, + const string &aPrecision, const vector &aCoresGPUsNumber, const int &aBand, + const int &aMaxRank, const vector &aInitialTheta, + const vector > &aLowerUpperBounds, const vector &aEstimatedTheta, + const string &aVerbose, const string &aDimension, const int &aMaxMleIterations, + const double &aTolerance, const vector &aPrediction, const std::vector &aPath, const bool &aSaveData) { + + vector argStrings; + + // Program name or placeholder + argStrings.emplace_back("--ConfigurationsModuleR"); + // Convert each argument to string and add to argv + argStrings.push_back("--N=" + to_string(aProblemSize)); + argStrings.push_back("--kernel=" + aKernelName); + argStrings.push_back("--dts=" + to_string(aTileSize[0])); + argStrings.push_back("--lts=" + to_string(aTileSize[1])); + argStrings.push_back("--p=" + to_string(aP_QGrid[0])); + argStrings.push_back("--q=" + to_string(aP_QGrid[1])); + argStrings.push_back("--time_slot=" + to_string(aTimeSlot)); + argStrings.push_back("--computation=" + aComputation); + argStrings.push_back("--precision=" + aPrecision); + argStrings.push_back("--cores=" + to_string(aCoresGPUsNumber[0])); + argStrings.push_back("--gpus=" + to_string(aCoresGPUsNumber[1])); + argStrings.push_back("--band=" + to_string(aBand)); + argStrings.push_back("--max_rank=" + to_string(aMaxRank)); + argStrings.push_back("--iTheta=" + to_string(aInitialTheta[0]) + ":" + to_string(aInitialTheta[1]) + ":" + + to_string(aInitialTheta[2])); + argStrings.push_back( + "--lb=" + to_string(aLowerUpperBounds[0][0]) + ":" + to_string(aLowerUpperBounds[0][1]) + ":" + + to_string(aLowerUpperBounds[0][2])); + argStrings.push_back( + "--ub=" + to_string(aLowerUpperBounds[1][0]) + ":" + to_string(aLowerUpperBounds[1][1]) + ":" + + to_string(aLowerUpperBounds[1][2])); + argStrings.push_back("--eTheta=" + to_string(aEstimatedTheta[0]) + ":" + to_string(aEstimatedTheta[1]) + ":" + + to_string(aEstimatedTheta[2])); + argStrings.push_back("--verbose=" + aVerbose); + argStrings.push_back("--dimension=" + aDimension); + argStrings.push_back("--max_mle_iterations=" + to_string(aMaxMleIterations)); + argStrings.push_back("--tolerance=" + to_string(aTolerance)); + if(!aPath[0].empty()){ + argStrings.push_back("--log_path=" + aPath[0]); + } + if(!aPath[1].empty()){ + argStrings.push_back("--data_path=" + aPath[1]); + } + if(!aPath[2].empty()){ + argStrings.push_back("--observations_file=" + aPath[2]); + } + if(!aPath[3].empty()){ + argStrings.push_back("--recovery_file=" + aPath[3]); + } + if(aSaveData){ + argStrings.emplace_back("--log"); + } + // This means that ZMiss > 0, which means prediction is activated + if (aPrediction[0] > 0) { + argStrings.push_back("--ZMiss=" + to_string(aPrediction[0])); + + // if mspe is activated + if (aPrediction[1]) { + argStrings.emplace_back("--mspe"); + } + + // if idw is activated + if (aPrediction[2]) { + argStrings.emplace_back("--idw"); + } + + // if fisher is activated + if (aPrediction[3]) { + argStrings.emplace_back("--fisher"); + } + + // if mloe_mmom is activated + if (aPrediction[4]) { + argStrings.emplace_back("--mloe_mmom"); + } + } + // Now, allocate a char** array and copy the arguments into it. + // Argv ownership is passed to Configurations, and it's responsibility for freeing it. + char **argv = new char *[argStrings.size()]; + + for (size_t i = 0; i < argStrings.size(); ++i) { + argv[i] = new char[argStrings[i].size() + 1]; // +1 for the null terminator + std::strcpy(argv[i], argStrings[i].c_str()); + } + + auto configurations = new Configurations(); + configurations->InitializeArguments(argStrings.size(), argv, true); + return configurations; + } + + NumericVector R_GetLocationX(ExaGeoStatData *apData){ + // Obtain the pointer to the array of doubles + double* data = apData->GetLocations()->GetLocationX(); + int length = apData->GetLocations()->GetSize(); + // Create an empty NumericVector of the appropriate length + NumericVector vec(length); + + // Copy data from the double array to the NumericVector + std::copy(data, data + length, vec.begin()); + + return vec; + } + + NumericVector R_GetLocationY(ExaGeoStatData *apData){ + + // Obtain the pointer to the array of doubles + double* data = apData->GetLocations()->GetLocationY(); + int length = apData->GetLocations()->GetSize(); + // Create an empty NumericVector of the appropriate length + NumericVector vec(length); + + // Copy data from the double array to the NumericVector + std::copy(data, data + length, vec.begin()); + + return vec; + } + + NumericVector R_GetLocationZ(ExaGeoStatData *apData){ + + // Obtain the pointer to the array of doubles + double* data = apData->GetLocations()->GetLocationZ(); + int length = apData->GetLocations()->GetSize(); + // Create an empty NumericVector of the appropriate length + NumericVector vec(length); + + // Copy data from the double array to the NumericVector + std::copy(data, data + length, vec.begin()); + + return vec; + } + + NumericVector R_GetDescZValues(ExaGeoStatData *apData, const std::string &aType){ + DescriptorType descriptorType; + void *pDescriptor; + if(aType == "chameleon" || aType == "Chameleon"){ + descriptorType = CHAMELEON_DESCRIPTOR; + pDescriptor = apData->GetDescriptorData()->GetDescriptor(descriptorType, DESCRIPTOR_Z).chameleon_desc; + } + else if (aType == "hicma" || aType == "Hicma" || aType == "HICMA"){ +#ifdef USE_HICMA + descriptorType = HICMA_DESCRIPTOR; + pDescriptor = apData->GetDescriptorData()->GetDescriptor(descriptorType, DESCRIPTOR_Z).hicma_desc; +#else + throw runtime_error("Please enable HiCMA to use HiCMA descriptors."); +#endif + } + else { + throw domain_error("Invalid type of descriptor, please use chameleon or hicma."); + } + // Obtain the pointer to the array of doubles + double* data = apData->GetDescriptorData()->GetDescriptorMatrix(descriptorType, pDescriptor); + int length = apData->GetLocations()->GetSize(); + // Create an empty NumericVector of the appropriate length + NumericVector vec(length); + + // Copy data from the double array to the NumericVector + std::copy(data, data + length, vec.begin()); + + return vec; + } + + ExaGeoStatData *R_ExaGeoStatLoadData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData) { + + // Wrap the raw pointer in a unique_ptr + std::unique_ptr> apDataPtr(apData); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(*apHardware, *apConfigurations, apDataPtr); + + // Since ExaGeoStatLoadData takes a reference, apDataPtr still owns the object. + // We can safely return the raw pointer, but we need to release it from apDataPtr to avoid deletion. + return apDataPtr.release(); + } + + ExaGeoStatData *R_ExaGeoStatModelData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData, + Nullable aMeasurementsVector, + Nullable aLocationsX, + Nullable aLocationsY, + Nullable aLocationsZ) { + + std::unique_ptr> apDataPtr(apData); + double* pMeasurementsVectorPtr = nullptr; + if (aMeasurementsVector == nullptr || aMeasurementsVector.isNull()){ + // This was the only way to pass C++ tests, if we checked for aMeasurementsVector != nullptr a seg fault occurs + } + else{ + NumericVector mat(aMeasurementsVector.get()); + pMeasurementsVectorPtr = &mat[0]; // Get the raw pointer to the matrix data + + if (!aLocationsX.isNull()) { + double *pLocationsXPtr; + NumericVector mat_location(aLocationsX.get()); + pLocationsXPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationX(*pLocationsXPtr, mat_location.size()); + } + + if (aLocationsY.isNotNull()) { + double *pLocationsYPtr; + NumericVector mat_location(aLocationsY.get()); + pLocationsYPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationY(*pLocationsYPtr, mat_location.size()); + + } + + if (aLocationsZ == nullptr || aLocationsZ.isNotNull()) { + double *pLocationsZPtr; + NumericVector mat_location(aLocationsZ.get()); + pLocationsZPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationZ(*pLocationsZPtr, mat_location.size()); + } + } + + exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(*apHardware, *apConfigurations, apDataPtr, pMeasurementsVectorPtr); + return apDataPtr.release(); + } + + ExaGeoStatData *R_ExaGeoStatPredictData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, + ExaGeoStatData *apData, + Nullable aMeasurementsVector, + Nullable aLocationsX, + Nullable aLocationsY, + Nullable aLocationsZ) { + + std::unique_ptr> apDataPtr(apData); + double* pMeasurementsVectorPtr = nullptr; + if (aMeasurementsVector == nullptr || aMeasurementsVector.isNull()){ + // This was the only way to pass C++ tests, if we checked for aMeasurementsVector != nullptr a seg fault occurs + } + else{ + + NumericVector mat(aMeasurementsVector.get()); + pMeasurementsVectorPtr = &mat[0]; // Get the raw pointer to the matrix data + + if (!aLocationsX.isNull()) { + double *pLocationsXPtr; + NumericVector mat_location(aLocationsX.get()); + pLocationsXPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationX(*pLocationsXPtr, mat_location.size()); + } + + if (aLocationsY.isNotNull()) { + double *pLocationsYPtr; + NumericVector mat_location(aLocationsY.get()); + pLocationsYPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationY(*pLocationsYPtr, mat_location.size()); + + } + + if (aLocationsZ.isNotNull()) { + double *pLocationsZPtr; + NumericVector mat_location(aLocationsZ.get()); + pLocationsZPtr = &mat_location[0]; // Get the raw pointer to the matrix data + apData->GetLocations()->SetLocationZ(*pLocationsZPtr, mat_location.size()); + } + } + + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(*apHardware, *apConfigurations, apDataPtr, pMeasurementsVectorPtr); + return apDataPtr.release(); + } +} \ No newline at end of file diff --git a/src/Rcpp-adapters/RcppExports.cpp b/src/Rcpp-adapters/RcppExports.cpp new file mode 100644 index 00000000..97c0e9e0 --- /dev/null +++ b/src/Rcpp-adapters/RcppExports.cpp @@ -0,0 +1,45 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file RcppExports.cpp + * @brief Rcpp export definitions for ExaGeoStatCPP module. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author David Helmy + * @date 2023-01-20 +**/ + +#include + +using namespace Rcpp; + +#ifdef RCPP_USE_GLOBAL_ROSTREAM +Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); +Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); +#endif + +/** + * @brief Rcpp module boot function for ExaGeoStatCPP. + * @return An SEXP representing the Rcpp module. + */ +RcppExport SEXP _rcpp_module_boot_ExaGeoStatCPP(); + +/** + * @brief Array of R function call entries. + */ +static const R_CallMethodDef CallEntries[] = { + {"_rcpp_module_boot_ExaGeoStatCPP", (DL_FUNC) &_rcpp_module_boot_ExaGeoStatCPP, 0}, + {nullptr, nullptr, 0} +}; + +/** + * @brief R initialization function for ExaGeoStatCPP module. + * @param dll The DllInfo structure. + */ +RcppExport void R_init_ExaGeoStatCPP(DllInfo *dll) { + R_registerRoutines(dll, nullptr, CallEntries, nullptr, nullptr); + R_useDynamicSymbols(dll, FALSE); +} diff --git a/src/Rcpp-adapters/RcppModules.cpp b/src/Rcpp-adapters/RcppModules.cpp new file mode 100644 index 00000000..d7d3101b --- /dev/null +++ b/src/Rcpp-adapters/RcppModules.cpp @@ -0,0 +1,75 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file RcppModules.cpp + * @brief Rcpp module definitions for ExaGeoStatCPP. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author David Helmy + * @date 2023-01-20 +**/ + +#include + +#include +#include +#include +#include + +/** Expose C++ class to R to be able to use Wrap and As + * Allows C++ to Send and Receive Class object from R + **/ +RCPP_EXPOSED_CLASS(ExaGeoStatHardware) +RCPP_EXPOSED_CLASS(Configurations) +RCPP_EXPOSED_CLASS_NODECL(ExaGeoStatData) + +/** Expose C++ Object With the Given functions **/ +RCPP_MODULE(ExaGeoStatCPP) { + + /** ExaGeoStatCPP Class **/ + using namespace Rcpp; + + /** Hardware Class **/ + class_("Hardware") + .constructor() + .method("finalize_hardware", &ExaGeoStatHardware::FinalizeHardware, "Manually finalize the hardware"); + + /** Configurations Class **/ + class_("Configurations") + .constructor(); + + /** Data Class **/ + class_>("Data") + .constructor(); + + /** Configurations Function **/ + function("configurations_init", &exageostat::adapters::R_InitializeArguments, + List::create(_["n"], _["kernel"], _["tile_size"], _["p_q"] = IntegerVector::create(1, 1), + _["time_slot"] = 1, _["computation"] = "exact", _["precision"] = "double", + _["cores_gpus"] = IntegerVector::create(1, 0), _["band"] = 1, _["max_rank"] = 500, + _["iTheta"], _["lb_ub"], _["eTheta"] = IntegerVector::create(-1, -1, -1), + _["verbose"] = "standard", _["dimension"] = "2D", _["mle_itr"] = 0, _["tol"] = 4, + _["prediction"] = IntegerVector::create(0, 0, 0, 0, 0), + _["paths"] = StringVector::create("", "", "", ""), _["save_data"] = 0)); + + function("simulate_data", &exageostat::adapters::R_ExaGeoStatLoadData, + List::create(_["hardware"], _["config"], _["data"])); + function("model_data", &exageostat::adapters::R_ExaGeoStatModelData, + List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, + _["y"] = R_NilValue, _["z"] = R_NilValue)); + function("predict_data", &exageostat::adapters::R_ExaGeoStatPredictData, + List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, + _["y"] = R_NilValue, _["z"] = R_NilValue)); + +// function("get_locationsX", &exageostat::adapters::R_GetLocationX, List::create(_["data"])); +// +// function("get_locationsY", &exageostat::adapters::R_GetLocationY, List::create(_["data"])); +// + function("get_locationsZ", &exageostat::adapters::R_GetLocationZ, List::create(_["data"])); + +// function("get_Z_measurement_vector", &exageostat::adapters::R_GetDescZValues, List::create(_["data"], _["type"])); + +} diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index 9a434188..1c2960e6 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @brief CMake build script for the operators library, which includes the OperatorMethods base class and the OperatorFactory class for creating operators of different types. # @author Mahmoud ElKarargy # @date 2023-05-30 diff --git a/src/api/ExaGeoStat.cpp b/src/api/ExaGeoStat.cpp index 7a03121f..6106a4ff 100644 --- a/src/api/ExaGeoStat.cpp +++ b/src/api/ExaGeoStat.cpp @@ -6,9 +6,9 @@ /** * @file ExaGeoStat.cpp * @brief High-Level Wrapper class containing the static API for ExaGeoStat operations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-05-30 + * @date 2024-02-04 **/ #include @@ -20,16 +20,14 @@ using namespace std; using namespace nlopt; using namespace exageostat::api; -using namespace exageostat::configurations; using namespace exageostat::generators; using namespace exageostat::dataunits; -using namespace exageostat::hardware; using namespace exageostat::prediction; template void ExaGeoStat::ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfigurations, - std::unique_ptr> &aData) { + Configurations &aConfigurations, + std::unique_ptr> &aData) { LOGGER("** ExaGeoStat data generation **") // Register and create a kernel object @@ -45,7 +43,7 @@ void ExaGeoStat::ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, template T ExaGeoStat::ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - std::unique_ptr> &aData, T *apMeasurementsMatrix) { + std::unique_ptr> &aData, T *apMeasurementsMatrix) { LOGGER("** ExaGeoStat data Modeling **") // Register and create a kernel object @@ -95,7 +93,7 @@ ExaGeoStat::ExaGeoStatMLETileAPI(const std::vector &aTheta, std::vect template void ExaGeoStat::ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, T *apMeasurementsMatrix) { LOGGER("** ExaGeoStat data Prediction **") diff --git a/src/configurations/CMakeLists.txt b/src/configurations/CMakeLists.txt index d0bee479..5193edb9 100644 --- a/src/configurations/CMakeLists.txt +++ b/src/configurations/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief This file contains the CMake configuration for the data-units directory. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-01-31 diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index 58300e3d..b8bccee7 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -6,10 +6,10 @@ /** * @file Configurations.cpp * @brief This file defines the Configurations class which stores the configuration parameters for ExaGeoStat. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-01-31 + * @date 2024-02-04 **/ #ifdef USE_MPI @@ -18,20 +18,20 @@ #include #include -#include +#include using namespace std; -using namespace exageostat::configurations; using namespace exageostat::common; Verbose Configurations::mVerbosity = Verbose::STANDARD_MODE; bool Configurations::mIsThetaInit = false; +bool Configurations::mHeapAllocated = false; Configurations::Configurations() { // Set default values for arguments! - SetComputation(common::EXACT_DENSE); + SetComputation(EXACT_DENSE); SetCoresNumber(1); SetGPUsNumbers(0); SetPGrid(1); @@ -39,7 +39,7 @@ Configurations::Configurations() { SetMaxRank(1); SetIsOOC(false); SetKernelName(""); - SetDimension(common::Dimension2D); + SetDimension(Dimension2D); SetTimeSlot(1); SetProblemSize(0); SetDenseTileSize(0); @@ -49,7 +49,6 @@ Configurations::Configurations() { SetBand(0); SetLoggerPath(""); SetIsSynthetic(true); - SetIsCSV(false); vector theta; SetInitialTheta(theta); SetLowerBounds(theta); @@ -58,27 +57,26 @@ Configurations::Configurations() { SetSeed(0); SetLogger(false); SetUnknownObservationsNb(0); - SetMeanSquareError(0.0); SetApproximationMode(1); SetActualObservationsFilePath(""); SetRecoveryFile(""); - SetPrecision(common::DOUBLE); + SetPrecision(DOUBLE); SetIsMSPE(false); SetIsFisher(false); SetIsIDW(false); SetIsMLOEMMOM(false); SetDataPath(""); - SetDistanceMetric(common::EUCLIDEAN_DISTANCE); + SetDistanceMetric(EUCLIDEAN_DISTANCE); SetAccuracy(0); SetIsNonGaussian(false); mIsThetaInit = false; } - -void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { +void Configurations::InitializeArguments(const int &aArgC, char **apArgV, const bool &aEnableR) { this->mArgC = aArgC; this->mpArgV = apArgV; + mHeapAllocated = aEnableR; int rank = 0; #ifdef USE_MPI @@ -225,19 +223,14 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV) { SetIsNonGaussian(true); } - if (GetLogger()) { - //initlog - InitLog(); - } this->PrintSummary(rank); - } void Configurations::InitializeAllTheta() { if (!mIsThetaInit) { - int parameters_number = kernels::KernelsConfigurations::GetParametersNumberKernelMap()[this->GetKernelName()]; + int parameters_number = exageostat::kernels::KernelsConfigurations::GetParametersNumberKernelMap()[this->GetKernelName()]; InitTheta(GetInitialTheta(), parameters_number); SetInitialTheta(GetInitialTheta()); @@ -289,7 +282,6 @@ void Configurations::InitializeDataGenerationArguments() { argument_name == "--data_path") { SetDataPath(argument_value); SetIsSynthetic(false); - SetIsCSV(true); } } } @@ -626,16 +618,6 @@ void Configurations::ParseDistanceMetric(const std::string &aDistanceMetric) { } } -void Configurations::InitLog() { - try { - SetFileLogPath(fopen(GetFileLogName().c_str(), "w+")); - } - catch (std::exception &e) { - SetFileLogPath(fopen("log_file", "w+")); - } - fprintf(GetFileLogPath(), "\t\tlog file is generated by ExaGeoStat application\n"); - fprintf(GetFileLogPath(), "\t\t============================================\n"); -} void Configurations::InitTheta(vector &aTheta, const int &size) { @@ -718,4 +700,11 @@ Configurations::~Configurations() { // Finalize the MPI environment. MPI_Finalize(); #endif + if (mHeapAllocated) { + for (size_t i = 0; i < this->mArgC; ++i) { + delete[] this->mpArgV[i]; // Delete each string + } + delete[] this->mpArgV; // Delete the array of pointers + } + this->mpArgV = nullptr; } diff --git a/src/data-generators/CMakeLists.txt b/src/data-generators/CMakeLists.txt index 4deb9b32..b5669976 100644 --- a/src/data-generators/CMakeLists.txt +++ b/src/data-generators/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief CMake configuration file for Data Generators module -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2024-02-04 diff --git a/src/data-generators/DataGenerator.cpp b/src/data-generators/DataGenerator.cpp index 8344241a..342a95b5 100644 --- a/src/data-generators/DataGenerator.cpp +++ b/src/data-generators/DataGenerator.cpp @@ -6,9 +6,9 @@ /** * @file DataGenerator.cpp * @brief Implementation of DataGenerator class - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-02-14 + * @date 2024-02-04 **/ #include @@ -20,32 +20,21 @@ using namespace exageostat::generators; using namespace exageostat::dataLoader::csv; using namespace exageostat::generators::synthetic; using namespace exageostat::dataunits; -using namespace exageostat::configurations; using namespace exageostat::common; template std::unique_ptr> DataGenerator::CreateGenerator(Configurations &apConfigurations) { + //// TODO: In case of other file support, Then we can create another layer for the factory creation depending on the file size. // Check the used Data generation method, whether it's synthetic or real. - if(apConfigurations.GetIsSynthetic() && apConfigurations.GetIsCSV()){ - throw std::domain_error("Please activate either the synthetic or the CSV file for data generation, but not both."); - } - if(!apConfigurations.GetIsSynthetic() && !apConfigurations.GetIsCSV()){ - throw std::domain_error("Please activate either the synthetic or the CSV file for data generation"); - } - if(apConfigurations.GetIsSynthetic()){ - aDataSourceType = SYNTHETIC; - } - else if(apConfigurations.GetIsCSV()){ - aDataSourceType = CSV_FILE; - } - results::Results::GetInstance()->SetIsSynthetic(apConfigurations.GetIsSynthetic()); + aDataSourceType = apConfigurations.GetIsSynthetic() ? SYNTHETIC : CSV_FILE; // Return DataGenerator unique pointer of Synthetic type - //// TODO: In case of other file support, Then we can create another layer for the factory creation depending on the file size. if (aDataSourceType == SYNTHETIC) { + results::Results::GetInstance()->SetIsSynthetic(true); return std::unique_ptr>(SyntheticGenerator::GetInstance()); } else if (aDataSourceType == CSV_FILE) { + results::Results::GetInstance()->SetIsSynthetic(false); return std::unique_ptr>(CSVLoader::GetInstance()); } else { throw std::runtime_error("Data Loading for this file type is unsupported for now"); @@ -65,4 +54,4 @@ DataGenerator::~DataGenerator() { } } -template DataSourceType DataGenerator::aDataSourceType = common::SYNTHETIC; +template DataSourceType DataGenerator::aDataSourceType = SYNTHETIC; diff --git a/src/data-generators/LocationGenerator.cpp b/src/data-generators/LocationGenerator.cpp index 47acd7fe..e1c25b8e 100644 --- a/src/data-generators/LocationGenerator.cpp +++ b/src/data-generators/LocationGenerator.cpp @@ -6,7 +6,7 @@ /** * @file LocationGenerator.cpp * @brief Generates and manages spatial locations for ExaGeoStat. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-02-04 **/ diff --git a/src/data-generators/concrete/CMakeLists.txt b/src/data-generators/concrete/CMakeLists.txt index 5be7a228..d80f538e 100644 --- a/src/data-generators/concrete/CMakeLists.txt +++ b/src/data-generators/concrete/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief CMake configuration file for the Synthetic Generator module -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-02-14 diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index c3500006..39708359 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -6,10 +6,10 @@ /** * @file SyntheticGenerator.cpp * @brief Implementation of the SyntheticGenerator class - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-02-14 + * @date 2024-02-04 **/ #include @@ -18,7 +18,6 @@ using namespace exageostat::generators::synthetic; using namespace exageostat::dataunits; using namespace exageostat::common; -using namespace exageostat::configurations; template SyntheticGenerator *SyntheticGenerator::GetInstance() { @@ -31,15 +30,15 @@ SyntheticGenerator *SyntheticGenerator::GetInstance() { template std::unique_ptr> -SyntheticGenerator::CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, +SyntheticGenerator::CreateData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) { int n = aConfigurations.GetProblemSize() * aConfigurations.GetTimeSlot(); auto data = std::make_unique>(n, aConfigurations.GetDimension()); + // Allocated new Locations object. auto *locations = new Locations(n, aConfigurations.GetDimension()); - int parameters_number = aKernel.GetParametersNumbers(); // Set initial theta values. diff --git a/src/data-loader/DataLoader.cpp b/src/data-loader/DataLoader.cpp index 6fc7dffa..d4da658f 100644 --- a/src/data-loader/DataLoader.cpp +++ b/src/data-loader/DataLoader.cpp @@ -6,7 +6,7 @@ /** * @file SyntheticGenerator.cpp * @brief Implementation of the SyntheticGenerator class - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-14 @@ -22,8 +22,8 @@ using namespace exageostat::common; template std::unique_ptr> -DataLoader::CreateData(exageostat::configurations::Configurations &aConfigurations, - const exageostat::hardware::ExaGeoStatHardware &aHardware, +DataLoader::CreateData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) { // create vectors that will be populated with read data. diff --git a/src/data-loader/concrete/CMakeLists.txt b/src/data-loader/concrete/CMakeLists.txt index 6bc7013a..72b458bd 100644 --- a/src/data-loader/concrete/CMakeLists.txt +++ b/src/data-loader/concrete/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief CMake configuration file for the data loader module -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-02-14 diff --git a/src/data-loader/concrete/CSVLoader.cpp b/src/data-loader/concrete/CSVLoader.cpp index 32a79331..a255f642 100644 --- a/src/data-loader/concrete/CSVLoader.cpp +++ b/src/data-loader/concrete/CSVLoader.cpp @@ -6,7 +6,7 @@ /** * @file CSVDataGenerator.cpp * @brief Implementation of the CSVDataGenerator class - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-14 @@ -18,8 +18,6 @@ using namespace std; -using namespace exageostat::configurations; -using namespace exageostat::hardware; using namespace exageostat::dataunits; using namespace exageostat::kernels; using namespace exageostat::common; @@ -140,6 +138,7 @@ void CSVLoader::ReadData(Configurations &aConfigurations, vector &aMeasure aConfigurations.SetProblemSize(index * aP / aConfigurations.GetTimeSlot()); file.close(); + LOGGER("Data is read from " << data_path << " successfully.") } template diff --git a/src/data-units/CMakeLists.txt b/src/data-units/CMakeLists.txt index d05e9096..ae2602f8 100644 --- a/src/data-units/CMakeLists.txt +++ b/src/data-units/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-02-27 diff --git a/src/data-units/DescriptorData.cpp b/src/data-units/DescriptorData.cpp index c74ee931..1e7857b1 100644 --- a/src/data-units/DescriptorData.cpp +++ b/src/data-units/DescriptorData.cpp @@ -6,7 +6,7 @@ /** * @file DescriptorData.cpp * @brief Contains the definition of the DescriptorData class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-18 diff --git a/src/data-units/ExaGeoStatData.cpp b/src/data-units/ExaGeoStatData.cpp index f64078e0..05185123 100644 --- a/src/data-units/ExaGeoStatData.cpp +++ b/src/data-units/ExaGeoStatData.cpp @@ -6,13 +6,14 @@ /** * @file ExaGeoStatData.cpp * @brief Contains the implementation of the ExaGeoStatData class. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-07-21 + * @date 2024-02-04 **/ #include +#include using namespace exageostat::dataunits; using namespace exageostat::common; @@ -23,6 +24,13 @@ ExaGeoStatData::ExaGeoStatData(const int &aSize, const Dimension &aDimension) this->mpDescriptorData = new DescriptorData(); } +template +ExaGeoStatData::ExaGeoStatData(const int &aSize, const std::string &aDimension) { + + this->mpLocations = new Locations(aSize, GetInputDimension(aDimension)); + this->mpDescriptorData = new DescriptorData(); +} + template ExaGeoStatData::~ExaGeoStatData() { delete this->mpLocations; @@ -70,7 +78,7 @@ void ExaGeoStatData::CalculateMedianLocations(const std::string &aKernelName, T x_min = this->mpLocations->GetLocationX()[0], x_max = this->mpLocations->GetLocationX()[0], y_min = this->mpLocations->GetLocationY()[0], y_max = this->mpLocations->GetLocationY()[0], z_min, z_max; - if (this->mpLocations->GetDimension() != common::Dimension2D) { + if (this->mpLocations->GetDimension() != Dimension2D) { z_min = this->mpLocations->GetLocationZ()[0]; z_max = this->mpLocations->GetLocationZ()[0]; } @@ -84,7 +92,7 @@ void ExaGeoStatData::CalculateMedianLocations(const std::string &aKernelName, y_min = (y < y_min) ? y : y_min; y_max = (y > y_max) ? y : y_max; - if (this->mpLocations->GetDimension() != common::Dimension2D) { + if (this->mpLocations->GetDimension() != Dimension2D) { T z = this->mpLocations->GetLocationX()[i]; z_min = (z < z_min) ? z : z_min; z_max = (z > z_max) ? z : z_max; @@ -93,13 +101,13 @@ void ExaGeoStatData::CalculateMedianLocations(const std::string &aKernelName, aLocations.GetLocationX()[0] = x_min + (x_max - x_min) / 2; aLocations.GetLocationY()[0] = y_min + (y_max - y_min) / 2; - if (this->mpLocations->GetDimension() != common::Dimension2D) { + if (this->mpLocations->GetDimension() != Dimension2D) { aLocations.GetLocationZ()[0] = z_min + (z_max - z_min) / 2; } } else { aLocations.GetLocationX()[0] = 0.5; aLocations.GetLocationY()[0] = 0.5; - if (this->mpLocations->GetDimension() != common::Dimension2D) { + if (this->mpLocations->GetDimension() != Dimension2D) { aLocations.GetLocationY()[0] = 0.5; } } diff --git a/src/data-units/Locations.cpp b/src/data-units/Locations.cpp index a2605ee3..3e511f8a 100644 --- a/src/data-units/Locations.cpp +++ b/src/data-units/Locations.cpp @@ -6,7 +6,7 @@ /** * @file Locations.cpp * @brief Implementation of the Locations class - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-02-27 diff --git a/src/data-units/descriptor/CMakeLists.txt b/src/data-units/descriptor/CMakeLists.txt index fd3cff70..316da3ef 100644 --- a/src/data-units/descriptor/CMakeLists.txt +++ b/src/data-units/descriptor/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-08-15 diff --git a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp index 19eeb6ab..c8ab42e2 100644 --- a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp +++ b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file ExaGeoStatDescriptor.cpp * @brief Implementation of creating matrix descriptors used in CHAMELEON and HiCMA libraries. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-07-17 diff --git a/src/data-units/descriptor/concrete/CMakeLists.txt b/src/data-units/descriptor/concrete/CMakeLists.txt index af483ee9..ca3190e8 100644 --- a/src/data-units/descriptor/concrete/CMakeLists.txt +++ b/src/data-units/descriptor/concrete/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @brief CMake build script for the descriptors library, which includes the concrete implementations of the # Descriptors class based on the enabled libraries (HiCMA or Chameleon). # @author Mahmoud ElKarargy diff --git a/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp b/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp index bb62abc8..706e66d9 100644 --- a/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp +++ b/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file ChameleonDescriptor.cpp * @brief Defines the ChameleonDescriptor class for creating matrix descriptors using the CHAMELEON library. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp index 88650700..9ba22f79 100644 --- a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp +++ b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file HicmaDescriptor.cpp * @brief Defines the Hicma Descriptor class for creating matrix descriptors using the HICMA library. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-08-15 diff --git a/src/hardware/CMakeLists.txt b/src/hardware/CMakeLists.txt index 4d5f98f4..40242b3b 100644 --- a/src/hardware/CMakeLists.txt +++ b/src/hardware/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief This file contains the CMake configuration for the configurations directory. -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-08-08 diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 1783a7f3..4a679cf4 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -6,10 +6,10 @@ /** * @file ExaGeoStatHardware.cpp * @brief Contains the implementation of the ExaGeoStatHardware class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2024-01-24 + * @date 2024-02-04 **/ #include @@ -17,13 +17,25 @@ #include #include #include -#include +#include +#include -using namespace exageostat::hardware; +using namespace exageostat::common; -ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, const int &aCoreNumber, +ExaGeoStatHardware::ExaGeoStatHardware(const Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber) { - this->mComputation = aComputation; + InitHardware(aComputation, aCoreNumber, aGpuNumber); +} + +// Constructor for R +ExaGeoStatHardware::ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber) { + + InitHardware(GetInputComputation(aComputation), aCoreNumber, aGpuNumber); +} + +void ExaGeoStatHardware::InitHardware(const Computation &aComputation, const int &aCoreNumber, + const int &aGpuNumber) { + LOGGER("** Initialise ExaGeoStat hardware **") int tag_width = 31, tag_sep = 26; // Init hardware using Chameleon @@ -33,8 +45,8 @@ ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, mpChameleonContext = chameleon_context_self(); } - // Init hardware using Hicma - if (aComputation == common::TILE_LOW_RANK) { + // Init hardware using HiCMA + if (aComputation == TILE_LOW_RANK) { #ifdef USE_HICMA if (!mpHicmaContext) { HICMA_user_tag_size(tag_width, tag_sep); @@ -45,28 +57,40 @@ ExaGeoStatHardware::ExaGeoStatHardware(const common::Computation &aComputation, throw std::runtime_error("You need to enable HiCMA to use TLR computation!"); #endif } - helpers::CommunicatorMPI::GetInstance()->SetHardwareInitialization(); - LOGGER("** Initialized ExaGeoStat hardware **") + exageostat::helpers::CommunicatorMPI::GetInstance()->SetHardwareInitialization(); +} + +void ExaGeoStatHardware::FinalizeHardware(){ + // finalize hardware using Chameleon + if (mpChameleonContext) { + CHAMELEON_Finalize() + mpChameleonContext = nullptr; + } + // finalize hardware using HiCMA +#ifdef USE_HICMA + if (mpHicmaContext) { + HICMA_Finalize(); + mpHicmaContext = nullptr; + } +#endif } ExaGeoStatHardware::~ExaGeoStatHardware() { - results::Results::GetInstance()->PrintEndSummary(); + exageostat::results::Results::GetInstance()->PrintEndSummary(); // finalize hardware using Chameleon if (mpChameleonContext) { CHAMELEON_Finalize() mpChameleonContext = nullptr; } // finalize hardware using HiCMA - if (this->mComputation == common::TILE_LOW_RANK) { #ifdef USE_HICMA if (mpHicmaContext) { HICMA_Finalize(); mpHicmaContext = nullptr; } #endif - } - helpers::CommunicatorMPI::GetInstance()->RemoveHardwareInitialization(); + exageostat::helpers::CommunicatorMPI::GetInstance()->RemoveHardwareInitialization(); } void *ExaGeoStatHardware::GetHicmaContext() { @@ -83,11 +107,11 @@ void *ExaGeoStatHardware::GetChameleonContext() { return mpChameleonContext; } -void *ExaGeoStatHardware::GetContext(common::Computation aComputation) { - if (aComputation == common::EXACT_DENSE || aComputation == common::DIAGONAL_APPROX) { +void *ExaGeoStatHardware::GetContext(Computation aComputation) { + if (aComputation == EXACT_DENSE || aComputation == DIAGONAL_APPROX) { return GetChameleonContext(); } - if (aComputation == common::TILE_LOW_RANK) { + if (aComputation == TILE_LOW_RANK) { return GetHicmaContext(); } return nullptr; diff --git a/src/helpers/BasselFunction.cpp b/src/helpers/BasselFunction.cpp index 79540be8..55634bd4 100644 --- a/src/helpers/BasselFunction.cpp +++ b/src/helpers/BasselFunction.cpp @@ -6,7 +6,7 @@ /** * @file BasselFunction * @brief This file contains the BasselFunction class which provides methods for computing derivatives of the modified Bessel function of the second kind. These functions are crucial in statistical and mathematical computations, especially in fields such as geostatistics and spatial analysis. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2024-01-24 diff --git a/src/helpers/ByteHandler.cpp b/src/helpers/ByteHandler.cpp index da38c2bb..6f848487 100644 --- a/src/helpers/ByteHandler.cpp +++ b/src/helpers/ByteHandler.cpp @@ -6,7 +6,7 @@ /** * @file ByteHandler.hpp * @brief Implementation of byte manipulation functions for ExaGeoStat. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2024-01-24 diff --git a/src/helpers/CMakeLists.txt b/src/helpers/CMakeLists.txt index af5f9ae7..550bc47d 100644 --- a/src/helpers/CMakeLists.txt +++ b/src/helpers/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-06-08 diff --git a/src/helpers/CommunicatorMPI.cpp b/src/helpers/CommunicatorMPI.cpp index 89f371f8..e527a8f9 100644 --- a/src/helpers/CommunicatorMPI.cpp +++ b/src/helpers/CommunicatorMPI.cpp @@ -6,7 +6,7 @@ /** * @file CommunicatorMPI.cpp * @brief Defines the CommunicatorMPI class for MPI rank communication. - * @version 1.0.1 + * @version 1.1.0 * @author Sameh Abdulah * @date 2023-11-10 **/ diff --git a/src/helpers/DiskWriter.cpp b/src/helpers/DiskWriter.cpp index 99a3668f..3cef39cf 100644 --- a/src/helpers/DiskWriter.cpp +++ b/src/helpers/DiskWriter.cpp @@ -6,7 +6,7 @@ /** * @file DiskWriter.cpp * @brief Contains the implementation of the DiskWriter class for writing data to disk. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/src/helpers/DistanceCalculationHelpers.cpp b/src/helpers/DistanceCalculationHelpers.cpp index 3ee132cb..33aac868 100644 --- a/src/helpers/DistanceCalculationHelpers.cpp +++ b/src/helpers/DistanceCalculationHelpers.cpp @@ -6,7 +6,7 @@ /** * @file DistanceCalculationHelpers.cpp * @brief Contains the implementation of the DistanceCalculationHelpers class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 diff --git a/src/kernels/CMakeLists.txt b/src/kernels/CMakeLists.txt index 290444d1..e76e4862 100644 --- a/src/kernels/CMakeLists.txt +++ b/src/kernels/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief This file contains the CMake configuration for the kernels directory. -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-04-11 diff --git a/src/kernels/Kernel.cpp b/src/kernels/Kernel.cpp index 193e3e04..78c992ee 100644 --- a/src/kernels/Kernel.cpp +++ b/src/kernels/Kernel.cpp @@ -6,7 +6,7 @@ /** * @file Kernel.cpp * @brief implementation file for the Kernels class, which contains the main kernel functions. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-12 diff --git a/src/kernels/concrete/BivariateMaternFlexible.cpp b/src/kernels/concrete/BivariateMaternFlexible.cpp index d70c75aa..9fc50947 100644 --- a/src/kernels/concrete/BivariateMaternFlexible.cpp +++ b/src/kernels/concrete/BivariateMaternFlexible.cpp @@ -6,7 +6,7 @@ /** * @file BivariateMaternFlexible.cpp * @brief Implementation of the BivariateMaternFlexible kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/BivariateMaternParsimonious.cpp b/src/kernels/concrete/BivariateMaternParsimonious.cpp index f222fe46..1431aec7 100644 --- a/src/kernels/concrete/BivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/BivariateMaternParsimonious.cpp @@ -6,7 +6,7 @@ /** * @file BivariateMaternParsimonious.cpp * @brief Implementation of the BivariateMaternParsimonious kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp index 8209086f..a3a49765 100644 --- a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp @@ -6,7 +6,7 @@ /** * @file BivariateSpacetimeMaternStationary.cpp * @brief Implementation of the BivariateSpacetimeMaternStationary kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/TrivariateMaternParsimonious.cpp b/src/kernels/concrete/TrivariateMaternParsimonious.cpp index 6f833b6e..1734861f 100644 --- a/src/kernels/concrete/TrivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/TrivariateMaternParsimonious.cpp @@ -6,7 +6,7 @@ /** * @file TrivariateMaternParsimonious.cpp * @brief Implementation of the BivariateMaternParsimonious kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateExpNonGaussian.cpp b/src/kernels/concrete/UnivariateExpNonGaussian.cpp index 6be4bf66..d207cbfc 100644 --- a/src/kernels/concrete/UnivariateExpNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateExpNonGaussian.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateExpNonGaussian.cpp * @brief Implementation of the UnivariateExpNonGaussian kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDbeta.cpp b/src/kernels/concrete/UnivariateMaternDbeta.cpp index caa2041a..fd42dec9 100644 --- a/src/kernels/concrete/UnivariateMaternDbeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDbeta.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDbeta.cpp * @brief Implementation of the UnivariateMaternDbeta kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp index 140c5659..c1370efa 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdbetaBeta.cpp * @brief Implementation of the UnivariateMaternDdbetaBeta kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp index ea9c173a..e85f3138 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdbetaNu.cpp * @brief Implementation of the UnivariateMaternDdbetaNu kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp index a54a1ce4..c1038762 100644 --- a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdnuNu.cpp * @brief Implementation of the UnivariateMaternDdnuNu kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp index ee980159..6d4af4b4 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquare.cpp * @brief Implementation of the UnivariateMaternDdsigmaSquare kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp index dc94d289..f6eacdc6 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquareBeta.cpp * @brief Implementation of the UnivariateMaternDdsigmaSquareBeta kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp index f6e62a31..1c934301 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDdsigmaSquareNu.cpp * @brief Implementation of the UnivariateMaternDdsigmaSquareNu kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDnu.cpp b/src/kernels/concrete/UnivariateMaternDnu.cpp index e357826c..e9e642ce 100644 --- a/src/kernels/concrete/UnivariateMaternDnu.cpp +++ b/src/kernels/concrete/UnivariateMaternDnu.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDnu.cpp * @brief Implementation of the UnivariateMaternDnu kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp index d6d55635..406f00be 100644 --- a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternDsigmaSquare.cpp * @brief Implementation of the UnivariateMaternDsigmaSquare kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp index d18410af..cdb458fe 100644 --- a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternNonGaussian.cpp * @brief Implementation of the UnivariateMaternNonGaussian kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp index a4a1d7f6..19a43a6f 100644 --- a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternNuggetsStationary.cpp * @brief Implementation of the UnivariateMaternNuggetsStationary kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateMaternStationary.cpp b/src/kernels/concrete/UnivariateMaternStationary.cpp index 9f576a8b..4facc1e3 100644 --- a/src/kernels/concrete/UnivariateMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternStationary.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateMaternStationary.cpp * @brief Implementation of the UnivariateMaternStationary kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp index b4631421..b7a7eb32 100644 --- a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp @@ -6,7 +6,7 @@ /** * @file UnivariateSpacetimeMaternStationary.cpp * @brief Implementation of the UnivariateSpacetimeMaternStationary kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-14 diff --git a/src/linear-algebra-solvers/CMakeLists.txt b/src/linear-algebra-solvers/CMakeLists.txt index d3aea1ca..0eb0f8a9 100644 --- a/src/linear-algebra-solvers/CMakeLists.txt +++ b/src/linear-algebra-solvers/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @brief CMake build script for the linear-algebra-solvers library, which includes the LinearAlgebraMethods base class and the LinearAlgebraFactory class for creating linear algebra solvers for different computations using HiCMA or Chameleon libraries. # @author Mahmoud ElKarargy # @author Sameh Abdulah diff --git a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp index 64949703..040d2e11 100644 --- a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp @@ -8,7 +8,7 @@ * @brief Implementation of the LinearAlgebraFactory class for creating linear algebra solvers for different computations using HiCMA or Chameleon libraries. * The factory creates a unique pointer to a concrete implementation of the LinearAlgebraMethods class based on the computation specified. * If the required library is not enabled, it throws a runtime_error exception. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-03-20 **/ @@ -27,7 +27,6 @@ using namespace exageostat::linearAlgebra; using namespace exageostat::common; -using namespace exageostat::configurations; template std::unique_ptr> LinearAlgebraFactory::CreateLinearAlgebraSolver(Computation aComputation) { diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index 60956afb..ec9558f5 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -6,10 +6,10 @@ /** * @file LinearAlgebraMethods.cpp * @brief Implementation of linear algebra methods. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-03-20 + * @date 2024-02-04 **/ #ifdef USE_MPI @@ -25,8 +25,6 @@ using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; using namespace exageostat::dataunits; -using namespace exageostat::configurations; -using namespace exageostat::hardware; // Define a method to set up the Chameleon descriptors template @@ -114,7 +112,7 @@ void LinearAlgebraMethods::InitiateDescriptors(Configurations &aConfiguration } template -void LinearAlgebraMethods::InitiateFisherDescriptors(configurations::Configurations &aConfigurations, +void LinearAlgebraMethods::InitiateFisherDescriptors(Configurations &aConfigurations, dataunits::DescriptorData &aDescriptorData) { // Check for initialize the Chameleon context. @@ -181,7 +179,7 @@ void LinearAlgebraMethods::InitiateFisherDescriptors(configurations::Configur template void LinearAlgebraMethods::InitiatePredictionDescriptors( - Configurations &aConfigurations, std::unique_ptr> &aData, const int &aP) { + Configurations &aConfigurations, std::unique_ptr> &aData, const int &aP) { if (!this->mpContext) { throw std::runtime_error( @@ -253,7 +251,7 @@ void LinearAlgebraMethods::InitiatePredictionDescriptors( template void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, const int &aP) { if (!this->mpContext) { @@ -316,9 +314,9 @@ void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfi } template -void LinearAlgebraMethods::GenerateSyntheticData(configurations::Configurations &aConfigurations, - const hardware::ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, +void LinearAlgebraMethods::GenerateSyntheticData(Configurations &aConfigurations, + const ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, const kernels::Kernel &aKernel) { this->mpContext = aHardware.GetChameleonContext(); @@ -330,7 +328,7 @@ void LinearAlgebraMethods::GenerateSyntheticData(configurations::Configuratio template void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, Locations *apLocation1, Locations *apLocation2, Locations *apLocation3, const int &aDistanceMetric, const kernels::Kernel &aKernel) { @@ -446,7 +444,7 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig } template -T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, +T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, const ExaGeoStatHardware &aHardware, Configurations &aConfiguration, Locations &aMissLocations, @@ -597,12 +595,12 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptr -T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, +T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const hardware::ExaGeoStatHardware &aHardware, - configurations::Configurations &aConfiguration, + const ExaGeoStatHardware &aHardware, + Configurations &aConfiguration, dataunits::Locations &aMissLocations, dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel) { @@ -795,7 +793,7 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< template void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, const ExaGeoStatHardware &aHardware, T *apTruthTheta, T *apEstimatedTheta, Locations &aMissLocations, Locations &aObsLocations, @@ -1128,9 +1126,9 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu } template -T *LinearAlgebraMethods::ExaGeoStatFisherTile(configurations::Configurations &aConfigurations, - std::unique_ptr> &aData, - const hardware::ExaGeoStatHardware &aHardware, T *apTheta, +T *LinearAlgebraMethods::ExaGeoStatFisherTile(Configurations &aConfigurations, + std::unique_ptr> &aData, + const ExaGeoStatHardware &aHardware, T *apTheta, const kernels::Kernel &aKernel) { this->SetContext(aHardware.GetChameleonContext()); diff --git a/src/linear-algebra-solvers/concrete/CMakeLists.txt b/src/linear-algebra-solvers/concrete/CMakeLists.txt index d2ef8862..af1c7e3e 100644 --- a/src/linear-algebra-solvers/concrete/CMakeLists.txt +++ b/src/linear-algebra-solvers/concrete/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @brief CMake build script for the linear-algebra-solvers library, which includes the concrete implementations of the # LinearAlgebraMethods class based on the enabled libraries (HiCMA or Chameleon). # @author Mahmoud ElKarargy diff --git a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp index 4c844585..9da5263e 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp @@ -7,10 +7,10 @@ * @file ChameleonImplementation.cpp * @brief This file contains the declaration of ChameleonImplementation class. * @details ChameleonImplementation is a concrete implementation of LinearAlgebraMethods class for dense or diagonal-super tile matrices.. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-03-20 + * @date 2024-02-04 **/ #ifdef USE_MPI @@ -25,7 +25,6 @@ using namespace std; using namespace exageostat::common; using namespace exageostat::dataunits; -using namespace exageostat::configurations; using namespace exageostat::linearAlgebra; template @@ -144,7 +143,7 @@ ChameleonImplementation::ExaGeoStatNonGaussianTransformTileAsync(void *apDesc } template -T ChameleonImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, +T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) { diff --git a/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp b/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp index 17009ca8..f29bc027 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp @@ -6,7 +6,7 @@ /** * @file ChameleonImplementationDense.cpp * @brief Dense Tile implementation of linear algebra methods. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 diff --git a/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp b/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp index b243f58e..2953d7c6 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp @@ -6,7 +6,7 @@ /** * @file ChameleonImplementationDST.cpp * @brief Diagonal Super Tile implementation of linear algebra methods. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-03-20 diff --git a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp index 2e86ec35..f26a4912 100644 --- a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp @@ -6,10 +6,10 @@ /** * @file HicmaImplementation.cpp * @brief Sets up the HiCMA descriptors needed for the tile low rank computations in ExaGeoStat. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-03-26 + * @date 2024-02-04 **/ #ifdef USE_MPI @@ -25,15 +25,13 @@ using namespace exageostat::common; using namespace exageostat::dataunits; using namespace exageostat::kernels; using namespace exageostat::helpers; -using namespace exageostat::hardware; -using namespace exageostat::configurations; int store_only_diagonal_tiles = 1; int use_scratch = 1; int global_check = 0; //used to create dense matrix for accuracy check template -void HicmaImplementation::SetModelingDescriptors(std::unique_ptr> &aData, +void HicmaImplementation::SetModelingDescriptors(std::unique_ptr> &aData, Configurations &aConfigurations, const int &aP) { int full_problem_size = aConfigurations.GetProblemSize() * aP; @@ -100,12 +98,12 @@ void HicmaImplementation::SetModelingDescriptors(std::unique_ptr -T HicmaImplementation::ExaGeoStatMLETile(const hardware::ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, +T HicmaImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const Kernel &aKernel) { - this->SetContext(hardware::ExaGeoStatHardware::GetContext(aConfigurations.GetComputation())); + this->SetContext(ExaGeoStatHardware::GetContext(aConfigurations.GetComputation())); if (!aData->GetDescriptorData()->GetIsDescriptorInitiated()) { this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(),aKernel.GetVariablesNumber(), apMeasurementsMatrix); } diff --git a/src/prediction/CMakeLists.txt b/src/prediction/CMakeLists.txt index 63a1f946..d43bde5c 100644 --- a/src/prediction/CMakeLists.txt +++ b/src/prediction/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-06-08 diff --git a/src/prediction/Prediction.cpp b/src/prediction/Prediction.cpp index 08804b55..4a955d96 100644 --- a/src/prediction/Prediction.cpp +++ b/src/prediction/Prediction.cpp @@ -6,10 +6,10 @@ /** * @file Prediction.cpp * @brief Contains the implementation of the Prediction class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-06-08 + * @date 2024-02-04 **/ #include @@ -18,12 +18,11 @@ #include using namespace exageostat::prediction; -using namespace exageostat::configurations; using namespace exageostat::dataunits; template -void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, +void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, + std::unique_ptr> &aData, Configurations &aConfigurations, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) { @@ -176,7 +175,7 @@ void Prediction::PredictMissingData(const hardware::ExaGeoStatHardware &aHard template void Prediction::InitializePredictionArguments(Configurations &aConfigurations, - std::unique_ptr> &aData, + std::unique_ptr> &aData, std::unique_ptr> &aLinearAlgebraSolver, T *apZObs, T *apZActual, Locations &aMissLocation, Locations &aObsLocation, T *apMeasurementsMatrix, const int &aP) { diff --git a/src/prediction/PredictionAuxiliaryFunctions.cpp b/src/prediction/PredictionAuxiliaryFunctions.cpp index 1dd7b33b..52fecd42 100644 --- a/src/prediction/PredictionAuxiliaryFunctions.cpp +++ b/src/prediction/PredictionAuxiliaryFunctions.cpp @@ -6,7 +6,7 @@ /** * @file PredictionAuxiliaryFunctions.cpp * @brief Contains the implementation of the PredictionAuxiliaryFunctions class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 @@ -16,7 +16,7 @@ #include #include -#include +#include using namespace exageostat::prediction; using namespace exageostat::dataunits; diff --git a/src/prediction/PredictionHelpers.cpp b/src/prediction/PredictionHelpers.cpp index 90da508b..d0f0f88e 100644 --- a/src/prediction/PredictionHelpers.cpp +++ b/src/prediction/PredictionHelpers.cpp @@ -6,7 +6,7 @@ /** * @file PredictionHelpers.cpp * @brief Contains the implementation of the PredictionHelpers class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-06-08 @@ -17,12 +17,11 @@ #include using namespace exageostat::prediction; -using namespace exageostat::configurations; using namespace exageostat::dataunits; template void PredictionHelpers::PickRandomPoints(Configurations &aConfigurations, - std::unique_ptr> &aData, T *apZObs, + std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, Locations &aMissLocation, Locations &aObsLocation, const int &aP) { diff --git a/src/results/CMakeLists.txt b/src/results/CMakeLists.txt index 8b7adbbc..72002da3 100644 --- a/src/results/CMakeLists.txt +++ b/src/results/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-09-14 diff --git a/src/results/Results.cpp b/src/results/Results.cpp index 63234f6c..d93d6ae7 100644 --- a/src/results/Results.cpp +++ b/src/results/Results.cpp @@ -6,13 +6,13 @@ /** * @file Results.cpp * @brief Defines the Results class for storing and accessing result data. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy - * @date 2023-09-14 + * @date 2024-02-04 **/ #include -#include +#include using namespace exageostat::results; using namespace exageostat::common; @@ -43,8 +43,8 @@ void Results::SetLoggerPath(const std::string &aLoggerPath) { void Results::PrintEndSummary() { - Verbose temp = exageostat::configurations::Configurations::GetVerbosity(); - exageostat::configurations::Configurations::SetVerbosity(STANDARD_MODE); + Verbose temp = Configurations::GetVerbosity(); + Configurations::SetVerbosity(STANDARD_MODE); LOGGER("") LOGGER("********************SUMMARY**********************") @@ -125,7 +125,7 @@ void Results::PrintEndSummary() { LOGGER("") } LOGGER("*************************************************") - exageostat::configurations::Configurations::SetVerbosity(temp); + Configurations::SetVerbosity(temp); } void Results::SetMLEIterations(int aIterationsNumber) { diff --git a/tests/R-tests/TestExaGeoStat.R b/tests/R-tests/TestExaGeoStat.R new file mode 100644 index 00000000..a1baac6b --- /dev/null +++ b/tests/R-tests/TestExaGeoStat.R @@ -0,0 +1,115 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# + +# @file TestExaGeoStat.R +# @brief +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-02-09 +# + +paste("---------------------------------------------------------------") +paste("ExaGeoStat with all Modules - Dense") +library("ExaGeoStatCPP") + +ncores <- 1 +ngpus <- 0 +problem_size <- 16 +dts <- 8 +lts <- 0 +computation <- "exact" +dimension = "2D" + +hardware <- new(Hardware, computation, ncores, ngpus) +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(6,1,1,1,1), dimension=dimension) +data_source <- new(Data, problem_size, dimension) + +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) +model_data(hardware=hardware, config=config, data=exageostat_data) +predict_data(hardware=hardware, config=config, data=exageostat_data) + +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with Data Generation - saving data with default path") + +dimension = "3D" +save_data <- TRUE +# if you didn't change the log_path it will be saved in the default path of project_path/synthetic_ds +log_path <- "" +# data path is where to read data from +data_path <- "" +# observations file path is where to read observation file +observations_file <- "" +# recovery file path is where to read recovery file +recovery_file <- "" +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data, dimension=dimension) +data_source <- new(Data, problem_size, dimension) +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) +## Print the data.. +#paste("Locations x") +#x <- get_locationsX(data=exageostat_data) +#print(x) +#paste("Locations y") +#y <- get_locationsY(data=exageostat_data) +#print(y) +paste("Locations z") +z <- get_locationsZ(data=exageostat_data) +print(z) +#paste("descZ measurements values") +#Z <- get_Z_measurement_vector(data=exageostat_data, type="chameleon") +#print(Z) + + +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with Data Generation - Reading Data") + +save_data <- FALSE +log_path <- "" +# data path is where to read data from +data_path <- "./synthetic_ds/SYN_16_1" +# observations file path is where to read observation file +observations_file <- "" +# recovery file path is where to read recovery file +recovery_file <- "" + +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data, dimension=dimension) +data_source <- new(Data, problem_size, dimension) +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) + + +paste("---------------------------------------------------------------------------------------------") + +paste("ExaGeoStat with data Modeling only") + +config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=10) +exageostat_data <- new(Data, problem_size, "2D") +numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, + 0.290822066007430102) +z_value = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + +numbers <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, + 0.877592126344701295) +locations_x = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + +numbers <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, + 0.942824444953078489) +locations_y = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) +model_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) + +paste("---------------------------------------------------------------") +paste("ExaGeoStat with data Prediction only - all prediction functions") +#config <- configurations_init(n=problem_size, kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,1,1,1,1)) +#predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index e220bf7f..d3170eff 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-01-24 @@ -22,6 +22,10 @@ if(USE_HICMA) add_subdirectory(data-units) endif () +if (USE_R) + add_subdirectory(Rcpp-adapters) +endif () + enable_testing() add_executable(exageostat-tests ${EXAGEOSTAT_TESTFILES}) target_link_libraries(exageostat-tests Catch2::Catch2WithMain ${PROJECT_NAME}_INTERFACE) diff --git a/tests/cpp-tests/Rcpp-adapters/CMakeLists.txt b/tests/cpp-tests/Rcpp-adapters/CMakeLists.txt new file mode 100644 index 00000000..1be993fa --- /dev/null +++ b/tests/cpp-tests/Rcpp-adapters/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-02-09 + +set(EXAGEOSTAT_TESTFILES + + ${CMAKE_CURRENT_SOURCE_DIR}/TestAllRFunctions.cpp + ${EXAGEOSTAT_TESTFILES} + PARENT_SCOPE + ) + diff --git a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp new file mode 100644 index 00000000..6a0a5182 --- /dev/null +++ b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file TestAllRFunctions.cpp + * @brief Test suite for R/Rcpp adapters in C++. + * @details This file contains tests for R methods adapted for use in C++ within the ExaGeoStat software package. + * It includes tests for initializing arguments, loading data, modeling data, and predicting data using Rcpp adapters. + * The test suite specifically verifies the integration and functionality of R methods through the Rcpp interface + * within a C++ environment, ensuring that statistical models and algorithms are correctly initialized, + * data is accurately loaded and processed, and predictions are properly executed. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-09 +**/ + +#include + +#include + +using namespace std; + +using namespace exageostat::adapters; + +void TEST_ALL_R_METHODS() { + SECTION("R METHODS") { + + auto configurations = R_InitializeArguments(16, "univariate_matern_stationary", {8, 0}, {1, 1}, 1, "exact", + "double", {1, 0}, 0, 1, {1, 0.1, 0.5}, {{0.1, 0.1, 0.1}, {5, 5, 5}}, + {-1, -1, -1}, "standard", "2D", 10, 4, {5, 1, 1, 1, 1}, + {"", "", "", ""}, false); + auto hardware = ExaGeoStatHardware("exact", 1, 0); + auto data_source = new ExaGeoStatData(16, "2D"); + auto exageostat_data = R_ExaGeoStatLoadData(&hardware, configurations, data_source); + R_ExaGeoStatModelData(&hardware, configurations, exageostat_data); + R_ExaGeoStatPredictData(&hardware, configurations, exageostat_data); + + delete exageostat_data; + delete configurations; + } +} + +TEST_CASE("Test R/Rcpp adapters in C++") { + TEST_ALL_R_METHODS(); +} \ No newline at end of file diff --git a/tests/cpp-tests/api/CMakeLists.txt b/tests/cpp-tests/api/CMakeLists.txt index 9748ef3d..4d46e039 100644 --- a/tests/cpp-tests/api/CMakeLists.txt +++ b/tests/cpp-tests/api/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @author Sameh Abdulah # @date 2023-08-08 diff --git a/tests/cpp-tests/api/TestExaGeoStatApi.cpp b/tests/cpp-tests/api/TestExaGeoStatApi.cpp index 4074803f..a2d84b9c 100644 --- a/tests/cpp-tests/api/TestExaGeoStatApi.cpp +++ b/tests/cpp-tests/api/TestExaGeoStatApi.cpp @@ -6,10 +6,10 @@ /** * @file TestExaGeoStatApi.cpp * @brief Test suite for the ExaGeoStat APIs data generation functionality. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-08-07 + * @date 2024-02-04 **/ #include @@ -18,8 +18,7 @@ using namespace std; using namespace exageostat::common; -using namespace exageostat::configurations; -using namespace exageostat::hardware; +using namespace exageostat::dataunits; void TEST_GENERATE_DATA() { SECTION("Data generation - Observations") @@ -47,7 +46,7 @@ void TEST_GENERATE_DATA() { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(EXACT_DENSE, 4, 0); // Or you could use configurations.GetComputation(). - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); @@ -108,7 +107,7 @@ void TEST_MODEL_DATA(Computation aComputation) { { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(aComputation, 4, 0); // Or you could use configurations.GetComputation(). - std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); //initiating the matrix of the CHAMELEON Descriptor Z. @@ -147,7 +146,7 @@ void TEST_MODEL_DATA(Computation aComputation) { { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(aComputation, 4, 0); // Or you could use configurations.GetComputation(). - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); double log_likelihood = exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, @@ -181,7 +180,7 @@ void TEST_PREDICTION() { Configurations::SetVerbosity(QUIET_MODE); auto hardware = ExaGeoStatHardware(EXACT_DENSE, configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); - std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); auto *z_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, diff --git a/tests/cpp-tests/configurations/CMakeLists.txt b/tests/cpp-tests/configurations/CMakeLists.txt index 5c54869a..9b7867f7 100644 --- a/tests/cpp-tests/configurations/CMakeLists.txt +++ b/tests/cpp-tests/configurations/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-31 diff --git a/tests/cpp-tests/configurations/TestConfigurations.cpp b/tests/cpp-tests/configurations/TestConfigurations.cpp index 59da0f29..f3b06e9a 100644 --- a/tests/cpp-tests/configurations/TestConfigurations.cpp +++ b/tests/cpp-tests/configurations/TestConfigurations.cpp @@ -10,7 +10,7 @@ * in the ExaGeoStat software package. The tests cover various setters, getters, and value checks * for configuration parameters such as dimensions, P-GRID, kernel name, problem size, precision, and more. * Additionally, the tests include a copy-constructor test for the Configurations class. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-01-31 **/ @@ -23,7 +23,6 @@ using namespace std; using namespace exageostat::common; -using namespace exageostat::configurations; void TEST_ARGUMENT_INITIALIZATION(){ diff --git a/tests/cpp-tests/data-generators/CMakeLists.txt b/tests/cpp-tests/data-generators/CMakeLists.txt index c3e1f4de..df341db5 100644 --- a/tests/cpp-tests/data-generators/CMakeLists.txt +++ b/tests/cpp-tests/data-generators/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-03-08 diff --git a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp index 83667823..1ada7d9c 100644 --- a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp @@ -9,7 +9,7 @@ * @details This file contains Catch2 unit tests that validate the functionality of the CSVDataGenerator class * in the ExaGeoStat software package. The tests cover various aspects of data generation, including spreading * and reversing bits, generating locations for different dimensions, and testing helper functions. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-03-08 **/ @@ -25,7 +25,6 @@ using namespace std; using namespace exageostat::generators; using namespace exageostat::dataunits; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::kernels; void TEST_CSV_P_1() { @@ -36,7 +35,6 @@ void TEST_CSV_P_1() { read_path = read_path + +"tests/cpp-tests/data-generators/concrete/synthetic_ds/SYN_16_1"; Configurations configurations; - configurations.SetIsCSV(true); configurations.SetIsSynthetic(false); configurations.SetProblemSize(16); configurations.SetDenseTileSize(8); @@ -49,7 +47,7 @@ void TEST_CSV_P_1() { Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); - auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); @@ -190,7 +188,6 @@ void TEST_CSV_P_2() { read_path = read_path + +"tests/cpp-tests/data-generators/concrete/synthetic_ds/SYN_16_1"; Configurations configurations; - configurations.SetIsCSV(true); configurations.SetIsSynthetic(false); configurations.SetProblemSize(16); configurations.SetDenseTileSize(8); @@ -201,7 +198,7 @@ void TEST_CSV_P_2() { Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); int p = pKernel->GetVariablesNumber(); - auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); @@ -362,7 +359,6 @@ void TEST_CSV_P_3() { read_path = read_path + +"tests/cpp-tests/data-generators/concrete/synthetic_ds/SYN_16_1"; Configurations configurations; - configurations.SetIsCSV(true); configurations.SetIsSynthetic(false); configurations.SetProblemSize(16); configurations.SetDenseTileSize(3); @@ -375,7 +371,7 @@ void TEST_CSV_P_3() { Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); int p = pKernel->GetVariablesNumber(); - auto hardware = exageostat::hardware::ExaGeoStatHardware(configurations.GetComputation(), + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); diff --git a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp index a86c4717..e6d02d74 100644 --- a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp @@ -9,7 +9,7 @@ * @details This file contains Catch2 unit tests that validate the functionality of the SyntheticGenerator class * in the ExaGeoStat software package. The tests cover various aspects of data generation, including spreading * and reversing bits, generating locations for different dimensions, and testing helper functions. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-03-08 **/ @@ -29,7 +29,6 @@ using namespace exageostat::generators::synthetic; using namespace exageostat::generators; using namespace exageostat::dataunits; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::kernels; using namespace exageostat::helpers; @@ -197,7 +196,7 @@ void TEST_GENERATE_LOCATIONS() { Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(synthetic_data_configurations.GetKernelName(), synthetic_data_configurations.GetTimeSlot()); - auto hardware = exageostat::hardware::ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), + auto hardware = ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), synthetic_data_configurations.GetCoresNumber(), synthetic_data_configurations.GetGPUsNumbers()); SECTION("2D Generation") @@ -300,7 +299,7 @@ void TEST_GENERATION() { synthetic_data_configurations.SetDenseTileSize(1); synthetic_data_configurations.SetKernelName("UnivariateMaternStationary"); synthetic_data_configurations.SetComputation(exageostat::common::EXACT_DENSE); - auto hardware = exageostat::hardware::ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), + auto hardware = ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), synthetic_data_configurations.GetCoresNumber(), synthetic_data_configurations.GetGPUsNumbers()); diff --git a/tests/cpp-tests/data-units/CMakeLists.txt b/tests/cpp-tests/data-units/CMakeLists.txt index 2abfab94..e08ed3ec 100644 --- a/tests/cpp-tests/data-units/CMakeLists.txt +++ b/tests/cpp-tests/data-units/CMakeLists.txt @@ -3,7 +3,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-01-24 diff --git a/tests/cpp-tests/data-units/TestDescriptorData.cpp b/tests/cpp-tests/data-units/TestDescriptorData.cpp index cdeb6f5f..ef332149 100644 --- a/tests/cpp-tests/data-units/TestDescriptorData.cpp +++ b/tests/cpp-tests/data-units/TestDescriptorData.cpp @@ -7,7 +7,7 @@ * @brief Tests for CHAMELEON to HICMA descriptor conversion in ExaGeoStat. * @details This test case verifies the conversion of matrix descriptors from the CHAMELEON format to the HICMA format. * It ensures that key properties of the matrix descriptor, such as dimensions, block sizes, and grid distribution parameters, are preserved during the conversion process. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-02-04 **/ @@ -20,9 +20,7 @@ using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::dataunits; -using namespace exageostat::hardware; void TEST_CHAM_TO_HICMA_CONV(){ diff --git a/tests/cpp-tests/hardware/CMakeLists.txt b/tests/cpp-tests/hardware/CMakeLists.txt index 059454b0..370d53d7 100644 --- a/tests/cpp-tests/hardware/CMakeLists.txt +++ b/tests/cpp-tests/hardware/CMakeLists.txt @@ -3,7 +3,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-01-24 diff --git a/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp b/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp index 89f1459f..a4b50332 100644 --- a/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp +++ b/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp @@ -7,7 +7,7 @@ * @file TestExaGeoStatHardware.cpp * @brief Unit tests for the ExaGeoStatHardware class in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the ExaGeoStatHardware class - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-01-24 **/ @@ -16,9 +16,7 @@ #include -using namespace exageostat::hardware; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_HARDWARE_CONSTRUCTION() { diff --git a/tests/cpp-tests/helpers/CMakeLists.txt b/tests/cpp-tests/helpers/CMakeLists.txt index b2943b6c..4f093909 100644 --- a/tests/cpp-tests/helpers/CMakeLists.txt +++ b/tests/cpp-tests/helpers/CMakeLists.txt @@ -3,7 +3,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-01-24 diff --git a/tests/cpp-tests/helpers/TestDiskWriter.cpp b/tests/cpp-tests/helpers/TestDiskWriter.cpp index ee3d1450..0916ad65 100644 --- a/tests/cpp-tests/helpers/TestDiskWriter.cpp +++ b/tests/cpp-tests/helpers/TestDiskWriter.cpp @@ -7,7 +7,7 @@ * @file TestDiskWriter.cpp * @brief Unit tests for the DiskWriter in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the class DiskWriter. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-01-24 **/ diff --git a/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp b/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp index 8a7f5e62..c4d9caab 100644 --- a/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp +++ b/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp @@ -7,7 +7,7 @@ * @file TestDistanceCalculationHelpers.cpp * @brief Unit tests for the DistanceCalculationHelpers.cpp in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the class DistanceCalculationHelpers. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-01-24 **/ diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp index 48cee8bb..74bf95a8 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp @@ -7,7 +7,7 @@ * @brief Unit tests for the BivariateMaternFlexible kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the BivariateMaternFlexible kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-09 @@ -19,10 +19,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_BivariateMaternFlexible() { @@ -49,7 +47,7 @@ void TEST_KERNEL_GENERATION_BivariateMaternFlexible() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp index cda682fe..2b8b5101 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestBivariateMaternParsimonious kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestBivariateMaternParsimonious kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_BivariateMaternParsimonious() { @@ -50,9 +48,8 @@ void TEST_KERNEL_GENERATION_BivariateMaternParsimonious() { int seed = 0; srand(seed); - std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + std::unique_ptr> data; + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations,data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp index 6b0d4387..c673306e 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestBivariateSpacetimeMaternStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestBivariateSpacetimeMaternStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_BivariateSpacetimeMaternStationary() { @@ -51,7 +49,7 @@ void TEST_KERNEL_GENERATION_BivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp index 4859dceb..e6d0057e 100644 --- a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestTrivariateMaternParsimonious kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestTrivariateMaternParsimonious kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_TrivariateMaternParsimonious() { @@ -48,7 +46,7 @@ void TEST_KERNEL_GENERATION_TrivariateMaternParsimonious() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp index 18abd91e..c1697056 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp @@ -6,7 +6,7 @@ /** * @file TestUnivariateExpNonGaussian.cpp * @brief - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp index 0ec31efd..1ed3d570 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDbeta kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDbeta kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -18,7 +18,7 @@ #include using namespace std; -using namespace exageostat::configurations; + using namespace exageostat::common; void TEST_KERNEL_GENERATION_UnivariateMaternDbeta() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp index 9102f9b6..d4cd5ecc 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdbetaBeta kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdbetaBeta kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,7 +19,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp index 903ef13a..d20272c2 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdbetaNu kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdbetaNu kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,10 +19,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariateMaternDdbetaNu() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp index 4e0b3a74..12ecc421 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdnuNu kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdnuNu kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,7 +19,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp index f3e9d072..a84bbdce 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdsigmaSquare kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdsigmaSquare kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -17,12 +17,11 @@ #include #include -using namespace exageostat::configurations; +using namespace std; + using namespace exageostat::common; using namespace exageostat::api; -using namespace std; - void TEST_KERNEL_GENERATION_UnivariateMaternDdsigmaSquare() { SECTION("UnivariateMaternDdsigmaSquare") diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp index 9f277794..053a222b 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdsigmaSquareBetaBeta kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdsigmaSquareBetaBeta kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -18,7 +18,7 @@ #include using namespace std; -using namespace exageostat::configurations; + using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp index 566786bc..22051826 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdsigmaSquareNu kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdsigmaSquareNu kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,7 +19,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp index 5d212512..7577b33d 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDnu kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDnu kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,7 +19,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp index dde48ff4..c38c9ac7 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternDdsigmaSquare kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternDdsigmaSquare kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-29 @@ -19,7 +19,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp index 31fbd107..27e9ce93 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternNonGaussian kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternNonGaussian kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,10 +19,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { @@ -52,7 +50,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp index 1f43a7fd..0142e74c 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternNuggetsStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternNuggetsStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariateMaternNuggetsStationary() { @@ -48,7 +46,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNuggetsStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp index a9b4777a..7f00ccd5 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateMaternStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateMaternStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-04-29 @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariateMaternStationary() { @@ -49,7 +47,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp index 8a63bd02..8d85d283 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp @@ -20,10 +20,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariatePowExpStationary() { @@ -49,7 +47,7 @@ void TEST_KERNEL_GENERATION_UnivariatePowExpStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp index 65147642..526022ba 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariateSpacetimeMaternStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariateSpacetimeMaternStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @date 2023-05-10 @@ -19,10 +19,8 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::api; using namespace exageostat::common; -using namespace exageostat::hardware; void TEST_KERNEL_GENERATION_UnivariateSpacetimeMaternStationary() { @@ -50,7 +48,7 @@ void TEST_KERNEL_GENERATION_UnivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, diff --git a/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt b/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt index f7afad0d..93440ee8 100644 --- a/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt +++ b/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-04-06 diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp index 8c956b1b..0b97a447 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp @@ -6,7 +6,7 @@ /** * @file TestChameleonImplementationDST.cpp * @brief Unit tests for the Diagonal Super Tile computation in the ExaGeoStat software package. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-04-09 **/ @@ -25,9 +25,7 @@ using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::dataunits; -using namespace exageostat::hardware; //Test that the function initializes the CHAM_descriptorC descriptor correctly. diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp index a7d2c204..61839081 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp @@ -6,7 +6,7 @@ /** * @file TestChameleonImplmentationDense.cpp * @brief Unit tests for the Dense computation in the ExaGeoStat software package. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-04-06 **/ @@ -25,9 +25,7 @@ using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::dataunits; -using namespace exageostat::hardware; //Test that the function initializes the CHAM_descriptorC descriptor correctly. void TEST_CHAMELEON_DESCRIPTORS_VALUES() { diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp index 9d0c7877..62a5ce94 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp @@ -6,7 +6,7 @@ /** * @file TestHiCMAImplementationTLR.cpp * @brief Unit tests for the Tile Low Rank computation in the ExaGeoStat software package. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-04-09 **/ @@ -18,9 +18,7 @@ using namespace std; using namespace exageostat::common; -using namespace exageostat::configurations; using namespace exageostat::dataunits; -using namespace exageostat::hardware; //Test that the function initializes the HICMA_descriptorC descriptor correctly. void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { @@ -52,7 +50,7 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { auto hardware = ExaGeoStatHardware(TILE_LOW_RANK, 1, 0); synthetic_data_configurations.SetApproximationMode(1); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, synthetic_data_configurations, data); @@ -86,7 +84,7 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { // initialize Hardware. auto hardware = ExaGeoStatHardware(TILE_LOW_RANK, 1, 0); - std::unique_ptr> data; + std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, synthetic_data_configurations, data); diff --git a/tests/cpp-tests/prediction/CMakeLists.txt b/tests/cpp-tests/prediction/CMakeLists.txt index 00fd6916..9a1d4bec 100644 --- a/tests/cpp-tests/prediction/CMakeLists.txt +++ b/tests/cpp-tests/prediction/CMakeLists.txt @@ -3,7 +3,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-01-24 diff --git a/tests/cpp-tests/prediction/TestPrediction.cpp b/tests/cpp-tests/prediction/TestPrediction.cpp index 98455d5a..74f1318f 100644 --- a/tests/cpp-tests/prediction/TestPrediction.cpp +++ b/tests/cpp-tests/prediction/TestPrediction.cpp @@ -19,8 +19,6 @@ using namespace std; using namespace exageostat::common; -using namespace exageostat::configurations; -using namespace exageostat::hardware; using namespace exageostat::prediction; void TEST_PREDICTION_MISSING_DATA() { @@ -48,7 +46,7 @@ void TEST_PREDICTION_MISSING_DATA() { auto hardware = ExaGeoStatHardware(EXACT_DENSE, configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); - std::unique_ptr> data = std::make_unique>( + std::unique_ptr> data = std::make_unique>( configurations.GetProblemSize(), configurations.GetDimension()); auto *z_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, @@ -84,8 +82,6 @@ void TEST_PREDICTION_MISSING_DATA() { vector estimated_theta{0.9, 0.09, 0.4}; configurations.SetEstimatedTheta(estimated_theta); - Prediction predictor; - // Register and create a kernel object exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( configurations.GetKernelName(), @@ -93,7 +89,7 @@ void TEST_PREDICTION_MISSING_DATA() { // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); - predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); REQUIRE(exageostat::results::Results::GetInstance()->GetMSPEError()== Catch::Approx(0.552448)); delete pKernel; @@ -106,9 +102,6 @@ void TEST_PREDICTION_MISSING_DATA() { vector estimated_theta{0.9, 0.09, 0.4}; configurations.SetEstimatedTheta(estimated_theta); - - Prediction predictor; - std::vector idw_error={ 1.18856255, 1.25725881, 1.11986628 }; // Register and create a kernel object @@ -117,7 +110,7 @@ void TEST_PREDICTION_MISSING_DATA() { configurations.GetTimeSlot()); // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); - predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); for(int i =0;i<3;i++){ REQUIRE(exageostat::results::Results::GetInstance()->GetIDWError()[i] == Catch::Approx(idw_error[i])); } @@ -132,14 +125,13 @@ void TEST_PREDICTION_MISSING_DATA() { vector estimated_theta{0.9, 0.09, 0.4}; configurations.SetEstimatedTheta(estimated_theta); - Prediction predictor; // Register and create a kernel object exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( configurations.GetKernelName(), configurations.GetTimeSlot()); // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); - predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); REQUIRE(exageostat::results::Results::GetInstance()->GetMLOE() == Catch::Approx(0.004467).margin(0.001)); REQUIRE(exageostat::results::Results::GetInstance()->GetMMOM() == Catch::Approx(-0.0812376).margin(0.001)); @@ -148,7 +140,7 @@ void TEST_PREDICTION_MISSING_DATA() { configurations.SetEstimatedTheta(new_estimated_theta1); // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); - predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); REQUIRE(exageostat::results::Results::GetInstance()->GetMLOE() == Catch::Approx(0).margin(0.001)); REQUIRE(exageostat::results::Results::GetInstance()->GetMMOM() == Catch::Approx(0).margin(0.001)); delete pKernel; @@ -162,14 +154,13 @@ void TEST_PREDICTION_MISSING_DATA() { vector new_estimated_theta{0.9, 0.09, 0.4}; configurations.SetEstimatedTheta(new_estimated_theta); - Prediction predictor; // Register and create a kernel object exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( configurations.GetKernelName(), configurations.GetTimeSlot()); // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); - predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); REQUIRE(exageostat::results::Results::GetInstance()->GetFisher00() == Catch::Approx(0.104589)); REQUIRE(exageostat::results::Results::GetInstance()->GetFisher11() == Catch::Approx(0.187355)); diff --git a/tests/cpp-tests/prediction/TestPredictionHelpers.cpp b/tests/cpp-tests/prediction/TestPredictionHelpers.cpp index eca6c9e3..8eeb92dc 100644 --- a/tests/cpp-tests/prediction/TestPredictionHelpers.cpp +++ b/tests/cpp-tests/prediction/TestPredictionHelpers.cpp @@ -7,7 +7,7 @@ * @file TestPredictionHelpers.cpp * @brief Unit tests for the TestPrediction class in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestPrediction class - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-12-08 **/ diff --git a/tests/cpp-tests/results/CMakeLists.txt b/tests/cpp-tests/results/CMakeLists.txt index 2be16bf6..4ef3c229 100644 --- a/tests/cpp-tests/results/CMakeLists.txt +++ b/tests/cpp-tests/results/CMakeLists.txt @@ -3,7 +3,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-01-24 diff --git a/tests/cpp-tests/results/TestResults.cpp b/tests/cpp-tests/results/TestResults.cpp index 72c4da1c..a847c126 100644 --- a/tests/cpp-tests/results/TestResults.cpp +++ b/tests/cpp-tests/results/TestResults.cpp @@ -6,7 +6,7 @@ * @file TestResults.cpp * @brief Unit tests for the Results class in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the Results class - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-01-24 **/ diff --git a/tests/heavy-tests/CMakeLists.txt b/tests/heavy-tests/CMakeLists.txt index b3feac02..f827077c 100644 --- a/tests/heavy-tests/CMakeLists.txt +++ b/tests/heavy-tests/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-12-20 diff --git a/tests/heavy-tests/ExamplesTests.cpp b/tests/heavy-tests/ExamplesTests.cpp index f87b46ec..7566ca7a 100644 --- a/tests/heavy-tests/ExamplesTests.cpp +++ b/tests/heavy-tests/ExamplesTests.cpp @@ -6,7 +6,7 @@ /** * @file TestExaGeoStatApi.cpp * @brief Test suite for the ExaGeoStat APIs data generation functionality. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-12-26 **/ @@ -19,7 +19,6 @@ using namespace std; using namespace std::filesystem; -using namespace exageostat::configurations; using namespace exageostat::dataunits; using namespace exageostat::api; diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index bb3f23a3..49c2d61e 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -6,7 +6,7 @@ /** * @file TestExaGeoStatApi.cpp * @brief Test suite for the ExaGeoStat APIs data generation functionality. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2023-12-20 **/ @@ -25,7 +25,6 @@ using namespace std; -using namespace exageostat::configurations; using namespace exageostat::dataunits; using namespace exageostat::api; using namespace exageostat::kernels; From ad9530d1934c75511306bacb65b4ca7a912bcd61 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 3 Mar 2024 20:49:33 +0200 Subject: [PATCH 37/82] tests:add r tests --- configure | 2 +- src/Rcpp-adapters/RcppModules.cpp | 9 +-- tests/R-tests/TestDataGeneration.R | 81 ++++++++++++++++++++ tests/R-tests/TestDataModeling.R | 51 +++++++++++++ tests/R-tests/TestDataPrediction.R | 70 ++++++++++++++++++ tests/R-tests/TestExaGeoStat.R | 114 ----------------------------- tests/R-tests/TestExaGeoStatAPI.R | 69 +++++++++++++++++ 7 files changed, 275 insertions(+), 121 deletions(-) create mode 100644 tests/R-tests/TestDataGeneration.R create mode 100644 tests/R-tests/TestDataModeling.R create mode 100644 tests/R-tests/TestDataPrediction.R delete mode 100644 tests/R-tests/TestExaGeoStat.R create mode 100644 tests/R-tests/TestExaGeoStatAPI.R diff --git a/configure b/configure index bbc31fc9..fb4b2e58 100755 --- a/configure +++ b/configure @@ -250,7 +250,7 @@ if [ "$USE_R" = "ON" ]; then } # Clean the directory and build the code with the specified options. - "$cmake_command_bin" --build . -j 2 + "$cmake_command_bin" --build . -j 6 if [ "$OS_TYPE" = "darwin"* ]; then cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.dylib" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.dylib -> src" diff --git a/src/Rcpp-adapters/RcppModules.cpp b/src/Rcpp-adapters/RcppModules.cpp index d7d3101b..efc6ea69 100644 --- a/src/Rcpp-adapters/RcppModules.cpp +++ b/src/Rcpp-adapters/RcppModules.cpp @@ -64,12 +64,9 @@ RCPP_MODULE(ExaGeoStatCPP) { List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, _["y"] = R_NilValue, _["z"] = R_NilValue)); -// function("get_locationsX", &exageostat::adapters::R_GetLocationX, List::create(_["data"])); -// -// function("get_locationsY", &exageostat::adapters::R_GetLocationY, List::create(_["data"])); -// + function("get_locationsX", &exageostat::adapters::R_GetLocationX, List::create(_["data"])); + function("get_locationsY", &exageostat::adapters::R_GetLocationY, List::create(_["data"])); function("get_locationsZ", &exageostat::adapters::R_GetLocationZ, List::create(_["data"])); - -// function("get_Z_measurement_vector", &exageostat::adapters::R_GetDescZValues, List::create(_["data"], _["type"])); + function("get_Z_measurement_vector", &exageostat::adapters::R_GetDescZValues, List::create(_["data"], _["type"])); } diff --git a/tests/R-tests/TestDataGeneration.R b/tests/R-tests/TestDataGeneration.R new file mode 100644 index 00000000..5831a657 --- /dev/null +++ b/tests/R-tests/TestDataGeneration.R @@ -0,0 +1,81 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# + +# @file TestDataGeneration.R +# @brief +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-02-09 + +library("ExaGeoStatCPP") + +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with Data Generation only - saving data with default path") + +dimension = "3D" +ncores <- 4 +ngpus <- 0 +problem_size <- 16 +dts <- 8 +lts <- 0 +computation <- "exact" + +save_data <- TRUE +# if you didn't change the log_path it will be saved in the default path of project_path/synthetic_ds +log_path <- "" +# data path is where to read data from +data_path <- "" +# observations file path is where to read observation file +observations_file <- "" +# recovery file path is where to read recovery file +recovery_file <- "" + +hardware <- new(Hardware, computation, ncores, ngpus) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data, dimension=dimension) +data_source <- new(Data, problem_size, dimension) + +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) +# Print the data.. +paste("** Locations x") +x <- get_locationsX(data=exageostat_data) +print(x) +paste("** Locations y") +y <- get_locationsY(data=exageostat_data) +print(y) +paste("** Locations z") +z <- get_locationsZ(data=exageostat_data) +print(z) +paste("** DescZ measurements values") +Z <- get_Z_measurement_vector(data=exageostat_data, type="chameleon") +print(Z) + +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with Data Generation - Reading Data") + +save_data <- FALSE +log_path <- "" +# data path is where to read data from +data_path <- "./synthetic_ds/SYN_16_1" +# observations file path is where to read observation file +observations_file <- "" +# recovery file path is where to read recovery file +recovery_file <- "" + +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data, dimension=dimension) +data_source <- new(Data, problem_size, dimension) +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) + +paste("** Locations x - after reading") +x <- get_locationsX(data=exageostat_data) +print(x) +paste("** Locations y - after reading") +y <- get_locationsY(data=exageostat_data) +print(y) +paste("** Locations z - after reading") +z <- get_locationsZ(data=exageostat_data) +print(z) +paste("** DescZ measurements values - after reading") +Z <- get_Z_measurement_vector(data=exageostat_data, type="chameleon") +print(Z) \ No newline at end of file diff --git a/tests/R-tests/TestDataModeling.R b/tests/R-tests/TestDataModeling.R new file mode 100644 index 00000000..9c6bad95 --- /dev/null +++ b/tests/R-tests/TestDataModeling.R @@ -0,0 +1,51 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# + +# @file TestDataGeneration.R +# @brief +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-02-09 + +library("ExaGeoStatCPP") + +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with data Modeling only - dst") + +dimension = "2D" +ncores <- 2 +ngpus <- 0 +problem_size <- 16 +dts <- 8 +lts <- 8 +computation <- "tlr" + +hardware <- new(Hardware, computation, ncores, ngpus) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=10) +exageostat_data <- new(Data, problem_size, dimension) +numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, + 0.290822066007430102) +z_value = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + +numbers <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, + 0.877592126344701295) +locations_x = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + +numbers <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, + 0.942824444953078489) +locations_y = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) +model_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) diff --git a/tests/R-tests/TestDataPrediction.R b/tests/R-tests/TestDataPrediction.R new file mode 100644 index 00000000..304ad653 --- /dev/null +++ b/tests/R-tests/TestDataPrediction.R @@ -0,0 +1,70 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# + +# @file TestDataPrediction.R +# @brief +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-02-09 + +library("ExaGeoStatCPP") + +paste("---------------------------------------------------------------") +paste("ExaGeoStat with data Prediction only - mspe") + +dimension = "2D" +ncores <- 2 +ngpus <- 0 +problem_size <- 16 +dts <- 8 +lts <- 0 +computation <- "exact" + +hardware <- new(Hardware, computation, ncores, ngpus) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,1,0,0,0)) +exageostat_data <- new(Data, problem_size, dimension) +numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, + 0.290822066007430102) +z_value = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + +numbers <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, + 0.877592126344701295) +locations_x = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + +numbers <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, + 0.942824444953078489) +locations_y = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) +predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) + +paste("---------------------------------------------------------------") +paste("ExaGeoStat with data Prediction only - idw") + +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,1,0,0)) +predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) + +paste("---------------------------------------------------------------") +paste("ExaGeoStat with data Prediction only - fisher") + +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,0,1,0)) +predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) + + +paste("---------------------------------------------------------------") +paste("ExaGeoStat with data Prediction only - MLOE-MMOM") + +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,0,0,1)) +predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) diff --git a/tests/R-tests/TestExaGeoStat.R b/tests/R-tests/TestExaGeoStat.R deleted file mode 100644 index b89fd45e..00000000 --- a/tests/R-tests/TestExaGeoStat.R +++ /dev/null @@ -1,114 +0,0 @@ - -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# - -# @file TestExaGeoStat.R -# @brief -# @version 1.1.0 -# @author Mahmoud ElKarargy -# @date 2024-02-09 -# - -paste("---------------------------------------------------------------") -paste("ExaGeoStat with all Modules - Dense") -library("ExaGeoStatCPP") - -ncores <- 1 -ngpus <- 0 -problem_size <- 16 -dts <- 8 -lts <- 0 -computation <- "exact" -dimension = "2D" - -hardware <- new(Hardware, computation, ncores, ngpus) -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(6,1,1,1,1), dimension=dimension) -data_source <- new(Data, problem_size, dimension) - -exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) -model_data(hardware=hardware, config=config, data=exageostat_data) -predict_data(hardware=hardware, config=config, data=exageostat_data) - -paste("---------------------------------------------------------------------------------------------") -paste("ExaGeoStat with Data Generation - saving data with default path") - -dimension = "3D" -save_data <- TRUE -# if you didn't change the log_path it will be saved in the default path of project_path/synthetic_ds -log_path <- "" -# data path is where to read data from -data_path <- "" -# observations file path is where to read observation file -observations_file <- "" -# recovery file path is where to read recovery file -recovery_file <- "" -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data, dimension=dimension) -data_source <- new(Data, problem_size, dimension) -exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) -## Print the data.. -#paste("Locations x") -#x <- get_locationsX(data=exageostat_data) -#print(x) -#paste("Locations y") -#y <- get_locationsY(data=exageostat_data) -#print(y) -paste("Locations z") -z <- get_locationsZ(data=exageostat_data) -print(z) -#paste("descZ measurements values") -#Z <- get_Z_measurement_vector(data=exageostat_data, type="chameleon") -#print(Z) - - -paste("---------------------------------------------------------------------------------------------") -paste("ExaGeoStat with Data Generation - Reading Data") - -save_data <- FALSE -log_path <- "" -# data path is where to read data from -data_path <- "./synthetic_ds/SYN_16_1" -# observations file path is where to read observation file -observations_file <- "" -# recovery file path is where to read recovery file -recovery_file <- "" - -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data, dimension=dimension) -data_source <- new(Data, problem_size, dimension) -exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) - - -paste("---------------------------------------------------------------------------------------------") -paste("ExaGeoStat with data Modeling only") - -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=10) -exageostat_data <- new(Data, problem_size, "2D") -numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, - -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, - 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, - -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, - 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, - 0.290822066007430102) -z_value = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) - -numbers <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, - 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, - 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, - 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, - 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, - 0.877592126344701295) -locations_x = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) - -numbers <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, - 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, - 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, - 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, - 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, - 0.942824444953078489) -locations_y = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) -model_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) - -paste("---------------------------------------------------------------") -paste("ExaGeoStat with data Prediction only - all prediction functions") -#config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,1,1,1,1)) -#predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) diff --git a/tests/R-tests/TestExaGeoStatAPI.R b/tests/R-tests/TestExaGeoStatAPI.R new file mode 100644 index 00000000..f14a2458 --- /dev/null +++ b/tests/R-tests/TestExaGeoStatAPI.R @@ -0,0 +1,69 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# + +# @file TestExaGeoStatAPI.R +# @brief +# @version 1.1.0 +# @author Mahmoud ElKarargy +# @date 2024-02-09 + +library("ExaGeoStatCPP") + +paste("---------------------------------------------------------------") +paste("ExaGeoStat with all Modules - Dense") + +ncores <- 1 +ngpus <- 0 +problem_size <- 16 +dts <- 8 +lts <- 0 +computation <- "exact" +dimension = "2D" + +hardware <- new(Hardware, computation, ncores, ngpus) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(6,1,1,1,1), dimension=dimension) +data_source <- new(Data, problem_size, dimension) + +exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) +model_data(hardware=hardware, config=config, data=exageostat_data) +predict_data(hardware=hardware, config=config, data=exageostat_data) + +predicit_value <- predict_data(locations_observation, location_missing) + + +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with Data Generation only") + +data_source <- new(Data, problem_size, dimension) +new_exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) + +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with data Modeling only") + +exageostat_data_modeling <- new(Data, problem_size, dimension) +numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, + 0.290822066007430102) +z_value = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + +numbers <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, + 0.877592126344701295) +locations_x = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + +numbers <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, + 0.942824444953078489) +locations_y = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) +model_data(hardware=hardware, config=config, data=exageostat_data_modeling, matrix=z_value, x=locations_x, y=locations_y) \ No newline at end of file From e119bb26126b21141c6cacd417832def68d314df Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 3 Mar 2024 21:19:07 +0200 Subject: [PATCH 38/82] fix tests --- inst/include/Rcpp-adapters/FunctionsAdapter.hpp | 2 +- src/Rcpp-adapters/FunctionsAdapter.cpp | 2 +- tests/R-tests/TestDataModeling.R | 12 +++++------- tests/R-tests/TestDataPrediction.R | 10 ++++------ tests/R-tests/TestExaGeoStatAPI.R | 13 ++++--------- 5 files changed, 15 insertions(+), 24 deletions(-) diff --git a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp index f570dd6d..41966ed7 100644 --- a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp +++ b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp @@ -59,7 +59,7 @@ namespace exageostat::adapters { const int &aMaxRank, const std::vector &aInitialTheta, const std::vector> &aLowerUpperBounds, const std::vector &aEstimatedTheta, const std::string &aVerbose, - const std::string &aDimension, const int &aMaxMleIterations, const double &aTolerance, + const std::string &aDimension, const int &aMaxMleIterations, const int &aTolerance, const std::vector &aPrediction, const std::vector &aPath, const bool &aSaveData ); diff --git a/src/Rcpp-adapters/FunctionsAdapter.cpp b/src/Rcpp-adapters/FunctionsAdapter.cpp index 521c5ccc..259e349b 100644 --- a/src/Rcpp-adapters/FunctionsAdapter.cpp +++ b/src/Rcpp-adapters/FunctionsAdapter.cpp @@ -33,7 +33,7 @@ namespace exageostat::adapters { const int &aMaxRank, const vector &aInitialTheta, const vector > &aLowerUpperBounds, const vector &aEstimatedTheta, const string &aVerbose, const string &aDimension, const int &aMaxMleIterations, - const double &aTolerance, const vector &aPrediction, const std::vector &aPath, const bool &aSaveData) { + const int &aTolerance, const vector &aPrediction, const std::vector &aPath, const bool &aSaveData) { vector argStrings; diff --git a/tests/R-tests/TestDataModeling.R b/tests/R-tests/TestDataModeling.R index 9c6bad95..ead99d73 100644 --- a/tests/R-tests/TestDataModeling.R +++ b/tests/R-tests/TestDataModeling.R @@ -23,29 +23,27 @@ lts <- 8 computation <- "tlr" hardware <- new(Hardware, computation, ncores, ngpus) -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=10) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5) exageostat_data <- new(Data, problem_size, dimension) -numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, 0.290822066007430102) -z_value = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) -numbers <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, 0.877592126344701295) -locations_x = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) -numbers <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, 0.942824444953078489) -locations_y = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + model_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) diff --git a/tests/R-tests/TestDataPrediction.R b/tests/R-tests/TestDataPrediction.R index 304ad653..82fe3c85 100644 --- a/tests/R-tests/TestDataPrediction.R +++ b/tests/R-tests/TestDataPrediction.R @@ -25,29 +25,27 @@ computation <- "exact" hardware <- new(Hardware, computation, ncores, ngpus) config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,1,0,0,0)) exageostat_data <- new(Data, problem_size, dimension) -numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, 0.290822066007430102) -z_value = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) -numbers <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, 0.877592126344701295) -locations_x = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) -numbers <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, 0.942824444953078489) -locations_y = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) paste("---------------------------------------------------------------") diff --git a/tests/R-tests/TestExaGeoStatAPI.R b/tests/R-tests/TestExaGeoStatAPI.R index f14a2458..440ccc83 100644 --- a/tests/R-tests/TestExaGeoStatAPI.R +++ b/tests/R-tests/TestExaGeoStatAPI.R @@ -30,9 +30,6 @@ exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_sou model_data(hardware=hardware, config=config, data=exageostat_data) predict_data(hardware=hardware, config=config, data=exageostat_data) -predicit_value <- predict_data(locations_observation, location_missing) - - paste("---------------------------------------------------------------------------------------------") paste("ExaGeoStat with Data Generation only") @@ -43,27 +40,25 @@ paste("------------------------------------------------------------------------- paste("ExaGeoStat with data Modeling only") exageostat_data_modeling <- new(Data, problem_size, dimension) -numbers <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, 0.290822066007430102) -z_value = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) -numbers <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, 0.877592126344701295) -locations_x = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) -numbers <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, 0.942824444953078489) -locations_y = matrix(numbers, nrow = 1, ncol = 16, byrow = TRUE) + model_data(hardware=hardware, config=config, data=exageostat_data_modeling, matrix=z_value, x=locations_x, y=locations_y) \ No newline at end of file From 258785b7493a93893153a6de9dbbfe8cb4ec62f8 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 3 Mar 2024 21:56:22 +0200 Subject: [PATCH 39/82] bug: in tlr --- Jenkinsfile | 6 ++++ inst/include/utilities/EnumStringParser.hpp | 2 +- tests/R-tests/TestDataModeling.R | 33 +++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 274dd7d3..a54d58e7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -103,6 +103,12 @@ pipeline { module load gcc/10.2.0 module load cmake/3.21.2 module load doxygen/1.8.20 + + module load hwloc/2.4.0-gcc-10.2.0 + module load openmpi/4.1.0-gcc-10.2.0 + module load starpu/1.3.9-gcc-10.2.0-mkl-openmpi-4.1.0 + module load gsl/2.6-gcc-10.2.0 + module load nlopt/2.7.0-gcc-10.2.0 #################################################### # BLAS/LAPACK #################################################### diff --git a/inst/include/utilities/EnumStringParser.hpp b/inst/include/utilities/EnumStringParser.hpp index 3f2746a9..cf05b89f 100644 --- a/inst/include/utilities/EnumStringParser.hpp +++ b/inst/include/utilities/EnumStringParser.hpp @@ -32,7 +32,7 @@ inline Computation GetInputComputation(std::string aComputation) { if (aComputation == "exact" || aComputation == "dense") { return EXACT_DENSE; - } else if (aComputation == "dst" || aComputation == "diagonal_super_tile") { + } else if (aComputation == "dst" || aComputation == "diag_approx") { return DIAGONAL_APPROX; } else if (aComputation == "tlr" || aComputation == "tile_low_rank") { return TILE_LOW_RANK; diff --git a/tests/R-tests/TestDataModeling.R b/tests/R-tests/TestDataModeling.R index ead99d73..9e97ed74 100644 --- a/tests/R-tests/TestDataModeling.R +++ b/tests/R-tests/TestDataModeling.R @@ -19,6 +19,39 @@ ncores <- 2 ngpus <- 0 problem_size <- 16 dts <- 8 +lts <- 0 +computation <- "diag_approx" + +hardware <- new(Hardware, computation, ncores, ngpus) +config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5) +exageostat_data <- new(Data, problem_size, dimension) +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, + 0.290822066007430102) + +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, + 0.877592126344701295) + +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, + 0.942824444953078489) + +model_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) +hardware$finalize_hardware() + +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with data Modeling only - tlr") + lts <- 8 computation <- "tlr" From 648645616316c6579d0d0d576a05d64a38dd5b58 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 3 Mar 2024 22:56:58 +0200 Subject: [PATCH 40/82] fix:tlr --- src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp index f26a4912..d1974a8c 100644 --- a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp @@ -428,7 +428,6 @@ int HicmaImplementation::ExaGeoStatMeasureDetTileAsync(void *apDescA, void *a for (m = 0; m < Z->mt; m++) { temp = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), STARPU_VALUE, &temp, sizeof(int), STARPU_R, ExaGeoStatDataGetAddr(Z, m, 0), STARPU_RW, ExaGeoStatDataGetAddr(det, 0, 0), From 9e9a28e276d735d0dac7455e1a3f81b4de8d59c2 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 3 Mar 2024 23:01:24 +0200 Subject: [PATCH 41/82] fix Jenkines --- Jenkinsfile | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a54d58e7..e4e98c02 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -103,16 +103,19 @@ pipeline { module load gcc/10.2.0 module load cmake/3.21.2 module load doxygen/1.8.20 - + #################################################### + # BLAS/LAPACK + #################################################### + module load mkl/2020.0.166 + #################################################### + # Dependencies + #################################################### module load hwloc/2.4.0-gcc-10.2.0 module load openmpi/4.1.0-gcc-10.2.0 module load starpu/1.3.9-gcc-10.2.0-mkl-openmpi-4.1.0 module load gsl/2.6-gcc-10.2.0 module load nlopt/2.7.0-gcc-10.2.0 - #################################################### - # BLAS/LAPACK - #################################################### - module load mkl/2020.0.166 + ./configure -e ./clean_build.sh cd bin From c61eaf1dd8336fb50abf4e953be1ee4fa4b98d3c Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 4 Mar 2024 14:51:50 +0200 Subject: [PATCH 42/82] fix: Jenkines in Documentation --- Jenkinsfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index e4e98c02..e5fd7e5f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -113,8 +113,6 @@ pipeline { module load hwloc/2.4.0-gcc-10.2.0 module load openmpi/4.1.0-gcc-10.2.0 module load starpu/1.3.9-gcc-10.2.0-mkl-openmpi-4.1.0 - module load gsl/2.6-gcc-10.2.0 - module load nlopt/2.7.0-gcc-10.2.0 ./configure -e ./clean_build.sh From 06220ccfacad4dab880ee9e9f198c78b1f47ad60 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 4 Mar 2024 15:48:01 +0200 Subject: [PATCH 43/82] minior updates --- examples/CMakeLists.txt | 3 ++- examples/data-loader/CMakeLists.txt | 2 +- examples/data-loader/CSVLoader.cpp | 2 +- examples/descriptors/CMakeLists.txt | 2 +- examples/descriptors/ChameleonDescriptor.cpp | 2 +- examples/descriptors/ChameleonToHicmaConverter.cpp | 2 +- examples/descriptors/HicmaDescriptor.cpp | 2 +- examples/hardware/CMakeLists.txt | 2 +- examples/hardware/ExaGeoStatHardware.cpp | 7 +++---- src/CMakeLists.txt | 10 +++++----- src/kernels/concrete/UnivariatePowExpStationary.cpp | 2 +- src/runtime/starpu/CMakeLists.txt | 6 ------ 12 files changed, 18 insertions(+), 24 deletions(-) delete mode 100644 src/runtime/starpu/CMakeLists.txt diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6166537d..7bc70451 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -10,8 +10,9 @@ # @date 2024-02-24 # Include subdirectories for end-to-end module, configurations module and data-generators module. -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/end-to-end) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/configurations) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-generators) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/data-loader) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/descriptors) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/end-to-end) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/hardware) diff --git a/examples/data-loader/CMakeLists.txt b/examples/data-loader/CMakeLists.txt index dc85167c..2268a81b 100644 --- a/examples/data-loader/CMakeLists.txt +++ b/examples/data-loader/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-02-14 diff --git a/examples/data-loader/CSVLoader.cpp b/examples/data-loader/CSVLoader.cpp index 47005a63..8cd90dcb 100644 --- a/examples/data-loader/CSVLoader.cpp +++ b/examples/data-loader/CSVLoader.cpp @@ -6,7 +6,7 @@ /** * @file CSVLoader.cpp * @brief Example of the CSVLoader class - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-02-18 **/ diff --git a/examples/descriptors/CMakeLists.txt b/examples/descriptors/CMakeLists.txt index 299ca8b4..7fc235e7 100644 --- a/examples/descriptors/CMakeLists.txt +++ b/examples/descriptors/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-02-14 diff --git a/examples/descriptors/ChameleonDescriptor.cpp b/examples/descriptors/ChameleonDescriptor.cpp index 13daf6ba..7b5927b4 100644 --- a/examples/descriptors/ChameleonDescriptor.cpp +++ b/examples/descriptors/ChameleonDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file ChameleonDescriptor.cpp * @brief Example file for the Chameleon descriptor. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-02-14 **/ diff --git a/examples/descriptors/ChameleonToHicmaConverter.cpp b/examples/descriptors/ChameleonToHicmaConverter.cpp index 03a1cfc9..299b9e89 100644 --- a/examples/descriptors/ChameleonToHicmaConverter.cpp +++ b/examples/descriptors/ChameleonToHicmaConverter.cpp @@ -6,7 +6,7 @@ /** * @file ChameleonDescriptor.cpp * @brief Example file for the Chameleon to Hicma Converter. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-02-14 **/ diff --git a/examples/descriptors/HicmaDescriptor.cpp b/examples/descriptors/HicmaDescriptor.cpp index 169dffac..395b795a 100644 --- a/examples/descriptors/HicmaDescriptor.cpp +++ b/examples/descriptors/HicmaDescriptor.cpp @@ -6,7 +6,7 @@ /** * @file HicmaDescriptor.cpp * @brief Example file for the Hicma descriptor. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-02-14 **/ diff --git a/examples/hardware/CMakeLists.txt b/examples/hardware/CMakeLists.txt index 3a0e5eb6..27da3399 100644 --- a/examples/hardware/CMakeLists.txt +++ b/examples/hardware/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @version 1.0.1 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-02-14 diff --git a/examples/hardware/ExaGeoStatHardware.cpp b/examples/hardware/ExaGeoStatHardware.cpp index 520f3a39..f08a4177 100644 --- a/examples/hardware/ExaGeoStatHardware.cpp +++ b/examples/hardware/ExaGeoStatHardware.cpp @@ -6,20 +6,19 @@ /** * @file TestExaGeoStatHardware.cpp * @briefExample for the ExaGeoStatHardware class in the ExaGeoStat software package. - * @version 1.0.1 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-02-14 **/ -#include #include #include +#include using namespace exageostat::common; -using namespace exageostat::hardware; -using namespace exageostat::configurations; int main(int argc, char **argv) { + LOGGER("** Example of Hardware **") // Initialize Configuration diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d5c353f..a7e5800e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,17 +11,17 @@ # @date 2024-02-04 # Add subdirectories for configurations, data-generators, data-units, and linear-algebra-solvers. +add_subdirectory(api) add_subdirectory(configurations) add_subdirectory(data-generators) +add_subdirectory(data-loader) add_subdirectory(data-units) -add_subdirectory(linear-algebra-solvers) -add_subdirectory(kernels) -add_subdirectory(api) -add_subdirectory(helpers) add_subdirectory(hardware) +add_subdirectory(helpers) +add_subdirectory(kernels) +add_subdirectory(linear-algebra-solvers) add_subdirectory(prediction) add_subdirectory(results) -add_subdirectory(data-loader) if (USE_R) add_subdirectory(Rcpp-adapters) diff --git a/src/kernels/concrete/UnivariatePowExpStationary.cpp b/src/kernels/concrete/UnivariatePowExpStationary.cpp index da225320..91339311 100644 --- a/src/kernels/concrete/UnivariatePowExpStationary.cpp +++ b/src/kernels/concrete/UnivariatePowExpStationary.cpp @@ -6,7 +6,7 @@ /** * @file UnivariatePowExpStationary.cpp * @brief Implementation of the UnivariatePowExpStationary kernel. - * @version 1.0.1 + * @version 1.1.0 * @author Sameh Abdulah * @author Mahmoud ElKarargy * @date 2023-04-14 diff --git a/src/runtime/starpu/CMakeLists.txt b/src/runtime/starpu/CMakeLists.txt deleted file mode 100644 index f08891df..00000000 --- a/src/runtime/starpu/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_subdirectory(concrete) -set(SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/StarPuFunctions.cpp - ${SOURCES} - PARENT_SCOPE -) From ff4ce3e69ae6c311a80dd75973127b7db43abbc8 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Fri, 29 Mar 2024 13:26:26 +0200 Subject: [PATCH 44/82] huge refactoring --- Jenkinsfile | 5 - README.md | 88 +- USER_MANUAL.md | 236 ++++- cmake/FindTMG.cmake | 308 +++++++ cmake/ImportBLAS.cmake | 15 +- cmake/ImportBLASPP.cmake | 28 +- cmake/ImportCatch2.cmake | 12 +- cmake/ImportChameleon.cmake | 17 +- cmake/ImportGSL.cmake | 16 +- cmake/ImportHCore.cmake | 15 +- cmake/ImportHiCMA.cmake | 16 +- cmake/ImportHwloc.cmake | 18 +- cmake/ImportLapack.cmake | 14 +- cmake/ImportNLOPT.cmake | 14 +- cmake/ImportStarPu.cmake | 22 +- cmake/ImportStarsH.cmake | 13 +- cmake/macros/BuildDependency.cmake | 38 +- cmake/macros/ImportDependency.cmake | 28 +- configure | 28 +- examples/configurations/CMakeLists.txt | 2 +- .../RunningWithDifferentConfigurations.cpp | 16 +- .../configurations/SetupConfigurations.cpp | 10 +- .../SyntheticDataGeneration.cpp | 13 +- examples/data-loader/CMakeLists.txt | 1 + examples/data-loader/CSVLoader.cpp | 19 +- examples/descriptors/CMakeLists.txt | 1 + examples/descriptors/ChameleonDescriptor.cpp | 16 +- .../descriptors/ChameleonToHicmaConverter.cpp | 18 +- examples/descriptors/HicmaDescriptor.cpp | 18 +- examples/end-to-end/CMakeLists.txt | 1 + examples/end-to-end/DataGeneration.cpp | 9 +- .../end-to-end/DataGenerationAndModeling.cpp | 10 +- .../DataGenerationAndPrediction.cpp | 10 +- .../DataGenerationModelingAndPrediction.cpp | 12 +- examples/end-to-end/DataModeling.cpp | 12 +- examples/end-to-end/DataPrediction.cpp | 10 +- examples/hardware/CMakeLists.txt | 1 + examples/hardware/ExaGeoStatHardware.cpp | 15 +- .../Rcpp-adapters/FunctionsAdapter.hpp | 105 ++- inst/include/api/ExaGeoStat.hpp | 18 +- .../include/configurations/Configurations.hpp | 502 ++++++----- .../include/data-generators/DataGenerator.hpp | 6 +- .../concrete/SyntheticGenerator.hpp | 3 +- inst/include/data-loader/DataLoader.hpp | 19 +- .../data-loader/concrete/CSVLoader.hpp | 11 +- .../data-units/ModelingDataHolders.hpp | 11 +- inst/include/helpers/DiskWriter.hpp | 55 -- .../concrete/UnivariatePowExpStationary.hpp | 2 +- .../LinearAlgebraMethods.hpp | 822 +----------------- .../chameleon/ChameleonImplementation.hpp | 93 +- .../hicma/tlr/HicmaImplementation.hpp | 51 +- inst/include/prediction/Prediction.hpp | 23 +- inst/include/prediction/PredictionHelpers.hpp | 2 +- inst/include/results/Results.hpp | 46 +- inst/include/runtime/RuntimeFunctions.hpp | 230 +++++ .../runtime/starpu/StarPuCodeletsHeaders.hpp | 26 + .../runtime/starpu/concrete/dcmg-codelet.hpp | 87 ++ .../runtime/starpu/concrete/ddotp-codelet.hpp | 80 ++ .../runtime/starpu/concrete/dmdet-codelet.hpp | 92 ++ .../starpu/concrete/dmloe-mmom-codelet.hpp | 83 ++ .../concrete/dmse-bivariate-codelet.hpp | 83 ++ .../runtime/starpu/concrete/dmse-codelet.hpp | 80 ++ .../starpu/concrete/dtrace-codelet.hpp | 91 ++ .../runtime/starpu/concrete/dzcpy-codelet.hpp | 82 ++ .../concrete/gaussian-to-non-codelet.hpp | 92 ++ .../concrete/non-gaussian-loglike-codelet.hpp | 93 ++ .../non-gaussian-transform-codelet.hpp | 130 +++ .../starpu/concrete/stride-vec-codelet.hpp | 81 ++ .../concrete/tri-stride-vec-codelet.hpp | 82 ++ .../runtime/starpu/helpers/StarPuHelpers.hpp | 114 +++ .../starpu/helpers/StarPuHelpersFactory.hpp | 42 + .../concrete/ChameleonStarPuHelpers.hpp | 108 +++ .../helpers/concrete/HicmaStarPuHelpers.hpp | 107 +++ inst/include/utilities/ErrorHandler.hpp | 2 - inst/include/utilities/Logger.hpp | 24 +- inst/include/utilities/Printer.hpp | 38 - man/Data.Rd | 40 + man/Hardware.Rd | 50 ++ man/fisher.Rd | 93 ++ man/get_Z_measurement_vector.Rd | 50 ++ man/get_locationsX.Rd | 49 ++ man/get_locationsY.Rd | 49 ++ man/get_locationsZ.Rd | 49 ++ man/idw.Rd | 98 +++ man/mloe_mmom.Rd | 98 +++ man/model_data.Rd | 107 +++ man/predict_data.Rd | 100 +++ man/simulate_data.Rd | 100 +++ src/CMakeLists.txt | 1 + src/Rcpp-adapters/FunctionsAdapter.cpp | 542 +++++++----- src/Rcpp-adapters/RcppModules.cpp | 39 +- src/api/ExaGeoStat.cpp | 31 +- src/configurations/Configurations.cpp | 15 +- src/data-generators/DataGenerator.cpp | 7 +- .../concrete/SyntheticGenerator.cpp | 18 +- src/data-loader/CMakeLists.txt | 2 +- src/data-loader/DataLoader.cpp | 13 +- src/data-loader/concrete/CSVLoader.cpp | 97 ++- src/hardware/ExaGeoStatHardware.cpp | 3 +- src/helpers/CMakeLists.txt | 1 - src/helpers/DiskWriter.cpp | 113 --- .../LinearAlgebraMethods.cpp | 731 +++++----------- .../chameleon/ChameleonImplementation.cpp | 411 ++------- .../chameleon/dense/ChameleonDense.cpp | 4 + .../concrete/tlr/HicmaImplementation.cpp | 211 +---- src/prediction/Prediction.cpp | 161 ++-- .../PredictionAuxiliaryFunctions.cpp | 2 +- src/prediction/PredictionHelpers.cpp | 1 + src/results/Results.cpp | 115 +-- src/runtime/CMakeLists.txt | 25 + src/runtime/parsec/CMakeLists.txt | 17 + src/runtime/parsec/ParsecFunctions.cpp | 87 ++ src/runtime/starpu/CMakeLists.txt | 55 ++ src/runtime/starpu/StarPuFunctions.cpp | 259 ++++++ src/runtime/starpu/concrete/dcmg-codelet.cpp | 88 ++ src/runtime/starpu/concrete/ddotp-codelet.cpp | 72 ++ src/runtime/starpu/concrete/dmdet-codelet.cpp | 90 ++ .../starpu/concrete/dmloe-mmom-codelet.cpp | 98 +++ .../concrete/dmse-bivariate-codelet.cpp | 87 ++ src/runtime/starpu/concrete/dmse-codelet.cpp | 71 ++ .../starpu/concrete/dtrace-codelet.cpp | 78 ++ src/runtime/starpu/concrete/dzcpy-codelet.cpp | 65 ++ .../concrete/gaussian-to-non-codelet.cpp | 93 ++ .../concrete/non-gaussian-loglike-codelet.cpp | 99 +++ .../non-gaussian-transform-codelet.cpp | 136 +++ .../starpu/concrete/stride-vec-codelet.cpp | 76 ++ .../concrete/tri-stride-vec-codelet.cpp | 83 ++ src/runtime/starpu/helpers/CMakeLists.txt | 23 + .../starpu/helpers/StarPuHelpersFactory.cpp | 38 + .../concrete/ChameleonStarPuHelpers.cpp | 61 ++ .../helpers/concrete/HicmaStarPuHelpers.cpp | 58 ++ tests/R-tests/TestDataGeneration.R | 22 +- tests/R-tests/TestDataModeling.R | 40 +- tests/R-tests/TestDataPrediction.R | 66 +- tests/R-tests/TestExaGeoStatAPI.R | 31 +- .../Rcpp-adapters/TestAllRFunctions.cpp | 149 +++- tests/cpp-tests/api/TestExaGeoStatApi.cpp | 34 +- .../configurations/TestConfigurations.cpp | 3 +- .../concrete/TestCSVDataGenerator.cpp | 39 +- .../concrete/TestSyntheticGenerator.cpp | 15 +- .../data-units/TestDescriptorData.cpp | 2 +- tests/cpp-tests/helpers/TestDiskWriter.cpp | 5 +- .../concrete/TestBivariateMaternFlexible.cpp | 4 +- .../TestBivariateMaternParsimonious.cpp | 3 +- ...TestBivariateSpacetimeMaternStationary.cpp | 4 +- .../TestTrivariateMaternParsimonious.cpp | 4 +- .../concrete/TestUnivariateMaternDbeta.cpp | 1 + .../TestUnivariateMaternDdbetaBeta.cpp | 1 + .../concrete/TestUnivariateMaternDdbetaNu.cpp | 1 + .../concrete/TestUnivariateMaternDdnuNu.cpp | 1 + .../TestUnivariateMaternDdsigmaSquare.cpp | 1 + .../TestUnivariateMaternDdsigmaSquareBeta.cpp | 1 + .../TestUnivariateMaternDdsigmaSquareNu.cpp | 1 + .../concrete/TestUnivariateMaternDnu.cpp | 1 + .../TestUnivariateMaternDsigmaSquare.cpp | 1 + .../TestUnivariateMaternNonGaussian.cpp | 4 +- .../TestUnivariateMaternNuggetsStationary.cpp | 4 +- .../TestUnivariateMaternStationary.cpp | 4 +- .../TestUnivariatePowExpStationary.cpp | 5 +- ...estUnivariateSpacetimeMaternStationary.cpp | 4 +- .../TestChameleonImplementationDST.cpp | 5 +- .../TestChameleonImplementationDense.cpp | 5 +- .../concrete/TestHiCMAImplementationTLR.cpp | 13 +- tests/cpp-tests/prediction/TestPrediction.cpp | 38 +- .../prediction/TestPredictionHelpers.cpp | 2 +- tests/cpp-tests/results/TestResults.cpp | 5 +- 166 files changed, 7106 insertions(+), 3314 deletions(-) create mode 100644 cmake/FindTMG.cmake delete mode 100644 inst/include/helpers/DiskWriter.hpp create mode 100644 inst/include/runtime/RuntimeFunctions.hpp create mode 100644 inst/include/runtime/starpu/StarPuCodeletsHeaders.hpp create mode 100644 inst/include/runtime/starpu/concrete/dcmg-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/ddotp-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/dmdet-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/dmloe-mmom-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/dmse-bivariate-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/dmse-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/dtrace-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/dzcpy-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/gaussian-to-non-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/non-gaussian-loglike-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/stride-vec-codelet.hpp create mode 100644 inst/include/runtime/starpu/concrete/tri-stride-vec-codelet.hpp create mode 100644 inst/include/runtime/starpu/helpers/StarPuHelpers.hpp create mode 100644 inst/include/runtime/starpu/helpers/StarPuHelpersFactory.hpp create mode 100644 inst/include/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.hpp create mode 100644 inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp delete mode 100644 inst/include/utilities/Printer.hpp create mode 100644 man/Data.Rd create mode 100644 man/Hardware.Rd create mode 100644 man/fisher.Rd create mode 100644 man/get_Z_measurement_vector.Rd create mode 100644 man/get_locationsX.Rd create mode 100644 man/get_locationsY.Rd create mode 100644 man/get_locationsZ.Rd create mode 100644 man/idw.Rd create mode 100644 man/mloe_mmom.Rd create mode 100644 man/model_data.Rd create mode 100644 man/predict_data.Rd create mode 100644 man/simulate_data.Rd delete mode 100644 src/helpers/DiskWriter.cpp create mode 100644 src/runtime/CMakeLists.txt create mode 100644 src/runtime/parsec/CMakeLists.txt create mode 100644 src/runtime/parsec/ParsecFunctions.cpp create mode 100644 src/runtime/starpu/CMakeLists.txt create mode 100644 src/runtime/starpu/StarPuFunctions.cpp create mode 100644 src/runtime/starpu/concrete/dcmg-codelet.cpp create mode 100644 src/runtime/starpu/concrete/ddotp-codelet.cpp create mode 100644 src/runtime/starpu/concrete/dmdet-codelet.cpp create mode 100644 src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp create mode 100644 src/runtime/starpu/concrete/dmse-bivariate-codelet.cpp create mode 100644 src/runtime/starpu/concrete/dmse-codelet.cpp create mode 100644 src/runtime/starpu/concrete/dtrace-codelet.cpp create mode 100644 src/runtime/starpu/concrete/dzcpy-codelet.cpp create mode 100644 src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp create mode 100644 src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp create mode 100644 src/runtime/starpu/concrete/non-gaussian-transform-codelet.cpp create mode 100644 src/runtime/starpu/concrete/stride-vec-codelet.cpp create mode 100644 src/runtime/starpu/concrete/tri-stride-vec-codelet.cpp create mode 100644 src/runtime/starpu/helpers/CMakeLists.txt create mode 100644 src/runtime/starpu/helpers/StarPuHelpersFactory.cpp create mode 100644 src/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.cpp create mode 100644 src/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.cpp diff --git a/Jenkinsfile b/Jenkinsfile index e5fd7e5f..bd84a4de 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -108,11 +108,6 @@ pipeline { #################################################### module load mkl/2020.0.166 #################################################### - # Dependencies - #################################################### - module load hwloc/2.4.0-gcc-10.2.0 - module load openmpi/4.1.0-gcc-10.2.0 - module load starpu/1.3.9-gcc-10.2.0-mkl-openmpi-4.1.0 ./configure -e ./clean_build.sh diff --git a/README.md b/README.md index b66daad7..f92e2f03 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,7 @@ maintaining traditional practices and embracing contemporary C++ elements like n Vision of ExaGeoStat/ExaGeoStatCPP -================= - +================================== The ExaGeoStat/ExaGeoStatCPP project is a collaboration between the KAUST Spatial Statistics group and the Extreme Computing Research Center (ECRC). Its contribution lies not in a new algorithm nor a new dataset, but in demonstrating the routine use of the larger datasets becoming available to geospatial @@ -55,9 +54,19 @@ numerical accuracy. This further expands practical problem sizes for statisticians with modest computational resources. +ExaGeoStatR : R wrapper of ExaGeoStat +===================================== + R is a powerful and versatile tool for scientific computing, offering a wide range of statistical and graphical + techniques, strong community support, and the flexibility to integrate with other programming languages. + Its open-source nature and extensive package ecosystem make it an invaluable resource for researchers and data scientists. + + Therefore, we decided to create ExaGeostatR: A wrapper for functionalities provided by ExaGeostat/ExaGeostatCPP to make use of R's various benefits. + More details about the exact usage are provided in the **```USER_MANUAL```**. + Current Version of ExaGeoStatCPP: 1.0.0 -====================== -Operations: +======================================= + +- ### Supported Operations: 1. (Data Generation): Generating large geospatial synthetic datasets using dense, Diagonal Super-Tile (DST) and Tile Low-Rank (TLR) approximation techniques. 2. (Data Modeling): Modeling large geospatial datasets on dense, Diagonal Super-Tile (DST) and Tile Low-Rank (TLR) approximation techniques through the Maximum likelihood Estimation (MLE) operation. @@ -65,8 +74,7 @@ Operations: 4. (MLOE/MMOM): Computing the Mean Loss of Efficiency (MLOE), Mean Misspecification of the Mean Square Error (MMOM), and Root mean square MOM (RMOM) to describe the prediction performance over the whole observation region. 5. (Fisher Information Matrix (FIM)): Quantifying the information content that a variable x carries about a parameter $\theta$ within a Gaussian distribution. -Supported Covariance Functions: -====================== +- ### Supported Covariance Functions: 1. Univariate Matérn (Gaussian/Stationary) 2. Univariate Matérn with Nugget (Gaussian/Stationary) @@ -78,12 +86,12 @@ Supported Covariance Functions: 8. Tukey g-and-h Univariate Matérn (non-Gaussian/Stationary) 9. Tukey g-and-h Univariate Power Exponential (non-Gaussian/Stationary) -Programming models: +- ### Programming models: 1. MPI 2. Task-based programming models -External libraries: +- ### External libraries: 1. NLOPT [https://nlopt.readthedocs.io/en/latest/](https://nlopt.readthedocs.io/en/latest/) 2. GSL [https://www.gnu.org/software/gsl/](https://www.gnu.org/software/gsl/) @@ -111,46 +119,76 @@ Project Hierarchy Installation ============ -Installation requires at least **CMake of version 3.2**. to build ExaGeoStatCPP, -please follow these instructions: +> Note: Installation requires at least **CMake of version 3.2**. to build ExaGeoStatCPP, -1. Get from git repository +To install the `ExaGeoStat` project locally, run the following commands in your terminal: - git clone git@github.com:ecrc/exageostatcpp +1. Clone the project from the remote github repository into your local machine using the following command - or + `git clone git@github.com:ecrc/exageostatcpp` for using SSH key or + + + `git clone https://github.com/ecrc/exageostatcpp` for using HTTPS - git clone https://github.com/ecrc/exageostatcpp +2. Change your current directory by getting into the `ExaGeoStatCPP` project directory -2. Go into the ExaGeoStatCPP folder + `cd exageostatcpp` - cd exageostatcpp -3. Run help of config.sh to know the needed arguments to run with your specific options. +3. Run `config.sh` script with the flag `-h` for help, to know the supported options and their corresponding flags. - ./config.sh --h + `./config.sh -h` or check user manual. -4. Run help of clean_build.sh to know the needed arguments to run with your specific options. - ./clean_build.sh -h +4. Run `clean_build.sh` script with the flag `-h` for help, to know the needed arguments to run with your specific options. -10. Export the installation paths of the dependencies, e.g., + `./clean_build.sh -h` - export PKG_CONFIG_PATH=$PWD/installdir/_deps/DEPENDENCY_NAME/lib/pkgconfig:$PKG_CONFIG_PATH - to your .bashrc file. +5. Export the installation paths of the dependencies to your `.bashrc` file, e.g. + + `export PKG_CONFIG_PATH=$PWD/installdir/_deps/DEPENDENCY_NAME/lib/pkgconfig:$PKG_CONFIG_PATH` Now, you can use the pkg-config executable to collect compiler and linker flags for EXAGEOSTATCPP. +- ## ExaGeoStatR Setup +Run the following commands in your terminal: + +1. Install `R` package using the package manager + + `apt install r-base r-base-dev` + + > `r-base` is the package that provides the base R installation, including the R interpreter, basic R packages, and the R development tools. The `r-base-dev` package, on the other hand, is an additional package that provides development tools for building R packages from source. It includes compilers and other tools necessary for compiling R packages from source code. + +2. Open the R prompt window by simply running `R` command in the terminal + + +3. Inside the prompt, we will install needed packages by running the following commads: + - `install.packages(Rcpp)` + + > Rcpp is a package that provides seamless integration between R and C++, allowing R users to write high-performance code in C++ and call it from R + + - `install.packages("assert")` + + > The assert package in R provides assertion functions, which are used to check assumptions in your code. + + +4. Return to the terminal and run the following command, make sure your current path is the ExaGeoStat project directory + + `R CMD INSTALL . --configure-args="r"` + + > This command installs our ExaGeoStat code as a package to be able to run it using R. We pass `-r`to the configuration to enable usage of R + + Using ExaGeoStatCPP -============ +=================== Please refer to **```USER_MANUAL```** for detailed instructions. Please take a look at the end-to-end examples as a reference for using all the operations. Contribute -======= +========== [Contribution Guidelines](CONTRIBUTING.md) diff --git a/USER_MANUAL.md b/USER_MANUAL.md index f0e6b849..128849c0 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -1,5 +1,5 @@ ExaGeoStatCPP User Manual -================ +========================= # Content @@ -222,45 +222,139 @@ for setting your arguments: synthetic_data_configurations.SetKernelName("BivariateSpacetimeMaternStationary"); synthetic_data_configurations.SetPrecision(exageostat::common::double); ``` -### Initialize the Hardware +### Initialize and Finalize the Hardware To use any operations, you must initialize the hardware by selecting the number of CPUs and/or GPUs. ```c++ +// Initialize an instance of the hardware auto hardware = ExaGeoStatHardware(computation, number of cores, number of gpus); + +// Other code goes here + +// Finalize the hardware instance. +hardware.FinalizeHardware() ``` +The subsequent arguments are as follows: + + - `computation`: Specifies the computation mode for the solver. + - `number of cores`: Indicates the number of CPU cores to be used for the solver. + - `number of gpus`: Specifies the number of GPUs to be used for the solver. -### Synthetic and Real Data -ExaGeoStatCPP can be used with different types of data, including: +##### *ExaGeoStatR wrapper* +```R +hardware <- new(Hardware, computation, number of cores, number of gpus); +hardware$finalize_hardware() +``` +First arguement represents the name of the R class that wrapps its correponding C++ class, the rest of arguments are the same as the C++ version + +### Types of Data -Synthetic data: Synthetic data is generated by the software according to the user arguments. +`ExaGeoStatCPP` can be used with 2 types of data: -Real data: ExaGeoStatCPP can also be used with real data, such as data from satellite imagery or weather sensors. Real data -can be used to train the software to predict the values of new data better. +- Synthetic data i.e. generated by the software according to the user arguments. +- Real data e.g. data from satellite imagery or weather sensors. Real data can be used to train the software to predict the values of new data better. -##### Synthetic Data Generation +#### Using Synthetic Data -After you provide your arguments with the Configurations module, you must do the following two steps: +Here we generate the data to be used by providing the needed arguments with the Configurations module, and then using the following code: ```c++ // Create a new ExaGeoStat data that holds the locations and descriptors data. std::unique_ptr> data; -// Generate data by passing your arguments through the configurations, hardware, and - container of the data, which will be filled with the newly generated data. -ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); + +// Generate data by passing your arguments through the configurations, hardware, +//and container of the data, which will be filled with the newly generated data. +ExaGeoStat::ExaGeoStatLoadData(configurations, data); ``` -##### Real Data +#### Using Real Data -- The Data Path must be passed to Configuration. +Here we use already existing data by providing the path to it: + +- The Data Path must be passed to Configuration +``` +--log_path= +``` + +And then using the following code: - --log_path= -- Then do the following two steps. ```c++ // Create a new ExaGeoStat data that holds the locations data and descriptors data. std::unique_ptr> data; + // Generate data by passing your arguments through the configurations, your hardware and your container of the data which will be filled with the new generated data. ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); ``` +The subsequent arguments are as follows: + + - `kernel`: Specifies the kernel function to be used in the simulation. + - `initial_theta`: Sets the initial values for the parameters of the simulation. + - `problem_size`: Defines the size of the problem or dataset to be simulated. + - `dts`: Specifies the time steps or intervals for the simulation. + - `dimension`: Indicates the dimensionality of the data to be simulated. + +### Location Getters + +ExaGeoStat supports locations data of dimension upto 3D, and therefore we have getters for X, Y and Z coordinates. + +#### X-coordinate Getter +```c++ +double *locations_x = exageostat_data->GetLocations()->GetLocationX(); +``` +##### *ExaGeoStatR wrapper* +```R +locations_x <- get_locationsX(data=exageostat_data) +``` +The subsequent arguments are as follows: + +- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data.. + +#### Y-coordinate Getter +```c++ +double *locations_y = exageostat_data->GetLocations()->GetLocationY(); +``` +##### *ExaGeoStatR wrapper* +```R +locations_y <- get_locationsY(data=exageostat_data) +``` +The subsequent arguments are as follows: + +- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data.. + +#### Z-coordinate Getter +```c++ +double *locations_z = exageostat_data->GetLocations()->GetLocationZ(); +``` +##### *ExaGeoStatR wrapper* +```R +locations_x <- get_locationsZ(data=exageostat_data) +``` +The subsequent arguments are as follows: + +- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data. + +#### Descriptive Z Values Getter +This function is used to retrieve descriptive Z values from ExaGeoStat data based on type of descriptor. +```c++ +//in this example, we use a chameleon descriptor. Similar code can be used for hicma descriptor. +DescriptorType descriptor_type = CHAMELEON_DESCRIPTOR; +void *descriptor = exageostat_data->GetDescriptorData()->GetDescriptor(descriptor_type, DESCRIPTOR_Z).chameleon_desc; +double *desc_Z_values = exagostat_data->GetDescriptorData()->GetDescriptorMatrix(descriptor_type, descriptor); +``` +The used variables are as follows: + +- `descriptor_type`: enum denoting the descriptor type,e.g. CHAMELEON_DESCRIPTOR,HICMA_DESCRIPTOR. +- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data. +- `desc_Z_values`: pointer to descriptor matrix. + +##### *ExaGeoStatR wrapper* +```R +desc_Z_values <- get_Z_measurement_vector(data=exageostat_data, type="chameleon") +``` +The subsequent arguments are as follows: + +- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data. +- `type`: string specifying the type of descriptor value to retrieve. ### Data Modeling @@ -270,39 +364,135 @@ To use data modeling, you have to do this operation. ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data, z_matrix); ``` + +##### *ExaGeoStatR wrapper* +```R +theta <- model_data(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, dimension=dimension, lb=lower_bound, ub=upper_bound, mle_itr=10, computation=computation) +``` +This function models data based on specified parameters: + + - `data`: The dataset to be used for modeling. + - `matrix`: The matrix of values to be used in the model. + - `x`: The x-coordinates of the data points. + - `y`: The y-coordinates of the data points. + - `kernel`: The kernel function to be applied in the model. + - `dts`: The time steps or intervals for the model. + - `dimension`: The dimensionality of the data. + - `lb`: The lower bound for the model parameters. + - `ub`: The upper bound for the model parameters. + - `mle_itr`: The number of iterations for maximum likelihood estimation. + - `computation`: The computation mode for the model. + + ### Data Prediction ```c++ //You have to pass your arguments through the configurations, your hardware, and your data. -ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); +ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); ``` + +##### *ExaGeoStatR wrapper* +```R +predict_data(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, z_miss=5) +``` +This function predicts data based on specified parameters: + + - `data`: The dataset to be used for prediction. + - `matrix`: The matrix of values to be used in the prediction. + - `x`: The x-coordinates of the data points. + - `y`: The y-coordinates of the data points. + - `kernel`: The kernel function to be applied in the prediction. + - `dts`: The time steps or intervals for the prediction. + - `estimated_theta`: The estimated parameters from the model. + - `z_miss`: The number of missing values to be handled in the prediction. + ### Fisher Function 1. Pass the fisher arguments to the Configurations. +``` +--fisher +``` - --fisher 2. Call the Data Prediction function. ```c++ // you have to pass your arguments through the configurations, your hardware and your data. -ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); +ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); +``` + + +##### *ExaGeoStatR wrapper* +```R +fisher(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, z_miss=5) ``` +This function predicts data based on specified parameters: + + - `data`: The dataset to be used for the Fisher method. + - `matrix`: The matrix of values to be used in the Fisher method. + - `x`: The x-coordinates of the data points. + - `y`: The y-coordinates of the data points. + - `kernel`: The kernel function to be applied in the Fisher method. + - `dts`: The time steps or intervals for the Fisher method. + - `estimated_theta`: The initial estimated parameters for the Fisher method. + - `z_miss`: The number of missing values to be handled in the Fisher method. + ### MLOE-MMOM Function 1. Pass the MLOE-MMOM arguments to the Configurations. - --mloe-mmom +``` +--mloe-mmom +``` + 2. Call the Data Prediction function. ```c++ // you have to pass your arguments through the configurations, your hardware and your data. -ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); +ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); +``` + + +##### *ExaGeoStatR wrapper* +```R +mloe_mmom(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, true_theta=estimated_theta, z_miss=5) ``` +This function predicts data based on specified parameters: + + - `data`: The dataset to be used for the MLE. + - `matrix`: The matrix of values to be used in the MLE. + - `x`: The x-coordinates of the data points. + - `y`: The y-coordinates of the data points. + - `kernel`: The kernel function to be applied in the MLE. + - `dts`: The time steps or intervals for the MLE. + - `estimated_theta`: The initial estimated parameters for the MLE. + - `true_theta`: The true parameters for the MLE, used for comparison or validation. + - `z_miss`: The number of missing values to be handled in the MLE. + ### IDW Function 1. Pass the IDW arguments to the Configurations. - --idw +``` +--idw +``` + 2. Call the Data Prediction function. ```c++ // you have to pass your arguments through the configurations, your hardware and your data. -ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); +ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); ``` + +##### *ExaGeoStatR wrapper* +```R +idw(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, z_miss=5) +``` +This function predicts data based on specified parameters: + + - `data`: The dataset to be used for the IDW. + - `matrix`: The matrix of values to be used in the IDW. + - `x`: The x-coordinates of the data points. + - `y`: The y-coordinates of the data points. + - `kernel`: The kernel function to be applied in the IDW. + - `dts`: The time steps or intervals for the IDW. + - `estimated_theta`: The initial estimated parameters for the IDW. + - `z_miss`: The number of missing values to be handled in the IDW. + ## Contributing [Contribution Guidelines](CONTRIBUTING.md) + - \ No newline at end of file diff --git a/cmake/FindTMG.cmake b/cmake/FindTMG.cmake new file mode 100644 index 00000000..bdf63250 --- /dev/null +++ b/cmake/FindTMG.cmake @@ -0,0 +1,308 @@ +### +# +# @copyright (c) 2009-2014 The University of Tennessee and The University +# of Tennessee Research Foundation. +# All rights reserved. +# @copyright (c) 2012-2016 Inria. All rights reserved. +# @copyright (c) 2012-2014 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria, Univ. Bordeaux. All rights reserved. +# @copyright (c) 2022 King Abdullah University of Science and Technology (KAUST). +# All rights reserved. +# +### +# +# - Find TMG include dirs and libraries +# Use this module by invoking find_package with the form: +# find_package(TMG +# [REQUIRED] # Fail with error if tmg is not found +# ) +# +# This module finds headers and tmg library. +# Results are reported in variables: +# TMG_FOUND - True if headers and requested libraries were found +# TMG_LINKER_FLAGS - list of required linker flags (excluding -l and -L) +# TMG_INCLUDE_DIRS - tmg include directories +# TMG_LIBRARY_DIRS - Link directories for tmg libraries +# TMG_LIBRARIES - tmg component libraries to be linked +# TMG_INCLUDE_DIRS_DEP - tmg + dependencies include directories +# TMG_LIBRARY_DIRS_DEP - tmg + dependencies link directories +# TMG_LIBRARIES_DEP - tmg libraries + dependencies +# +# The user can give specific paths where to find the libraries adding cmake +# options at configure (ex: cmake path/to/project -DTMG=path/to/tmg): +# TMG_DIR - Where to find the base directory of tmg +# TMG_INCDIR - Where to find the header files +# TMG_LIBDIR - Where to find the library files +# The module can also look for the following environment variables if paths +# are not given as cmake variable: TMG_DIR, TMG_INCDIR, TMG_LIBDIR + +#============================================================================= +# Copyright 2012-2013 Inria +# Copyright 2012-2013 Emmanuel Agullo +# Copyright 2012-2013 Mathieu Faverge +# Copyright 2012 Cedric Castagnede +# Copyright 2013-2016 Florent Pruvost +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file ECRC-Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of Ecrc, substitute the full +# License text for the above reference.) + + +if (NOT TMG_FOUND) + set(TMG_DIR "" CACHE PATH "Installation directory of TMG library") + if (NOT TMG_FIND_QUIETLY) + message(STATUS "A cache variable, namely TMG_DIR, has been set to specify the install directory of TMG") + endif() +endif() + + +# used to test a TMG function after +get_property(_LANGUAGES_ GLOBAL PROPERTY ENABLED_LANGUAGES) +if (NOT _LANGUAGES_ MATCHES Fortran) + include(CheckFunctionExists) +else (NOT _LANGUAGES_ MATCHES Fortran) + include(CheckFortranFunctionExists) +endif (NOT _LANGUAGES_ MATCHES Fortran) + +# TMG depends on LAPACK anyway, try to find it +if (NOT LAPACK_FOUND) + if(TMG_FIND_REQUIRED) + find_package(LAPACKEXT REQUIRED) + else() + find_package(LAPACKEXT) + endif() +endif() + +# TMG depends on LAPACK +if (LAPACK_FOUND) + + # check if a tmg function exists in the LAPACK lib + set(CMAKE_REQUIRED_LIBRARIES "${LAPACK_LINKER_FLAGS};${LAPACK_LIBRARIES}") + include(CheckFunctionExists) + include(CheckFortranFunctionExists) + unset(TMG_WORKS CACHE) + if (NOT _LANGUAGES_ MATCHES Fortran) + check_function_exists(dlarnv TMG_WORKS) + else (NOT _LANGUAGES_ MATCHES Fortran) + check_fortran_function_exists(dlarnv TMG_WORKS) + endif (NOT _LANGUAGES_ MATCHES Fortran) + if (TMG_WORKS) + unset(TMG_WORKS CACHE) + if (NOT _LANGUAGES_ MATCHES Fortran) + check_function_exists(dlagsy TMG_WORKS) + else (NOT _LANGUAGES_ MATCHES Fortran) + check_fortran_function_exists(dlagsy TMG_WORKS) + endif (NOT _LANGUAGES_ MATCHES Fortran) + mark_as_advanced(TMG_WORKS) + endif() + set(CMAKE_REQUIRED_LIBRARIES) + + if(TMG_WORKS) + if(NOT TMG_FIND_QUIETLY) + message(STATUS "Looking for tmg: test with lapack succeeds") + endif() + # test succeeds: TMG is in LAPACK + set(TMG_LIBRARIES "${LAPACK_LIBRARIES}") + if (LAPACK_LIBRARY_DIRS) + set(TMG_LIBRARY_DIRS "${LAPACK_LIBRARY_DIRS}") + endif() + if(LAPACK_INCLUDE_DIRS) + set(TMG_INCLUDE_DIRS "${LAPACK_INCLUDE_DIRS}") + endif() + if (LAPACK_LINKER_FLAGS) + set(TMG_LINKER_FLAGS "${LAPACK_LINKER_FLAGS}") + endif() + else() + + if(NOT TMG_FIND_QUIETLY) + message(STATUS "Looking for tmg : test with lapack fails") + message(STATUS "Looking for tmg : try to find it elsewhere") + endif() + # test fails: try to find TMG lib exterior to LAPACK + + # Looking for lib tmg + # ------------------- + + # Add system library paths to search lib + # -------------------------------------- + unset(_lib_env) + set(ENV_TMG_DIR "$ENV{TMG_DIR}") + set(ENV_TMG_LIBDIR "$ENV{TMG_LIBDIR}") + if(ENV_TMG_LIBDIR) + list(APPEND _lib_env "${ENV_TMG_LIBDIR}") + elseif(ENV_TMG_DIR) + list(APPEND _lib_env "${ENV_TMG_DIR}") + list(APPEND _lib_env "${ENV_TMG_DIR}/lib") + else() + if(WIN32) + string(REPLACE ":" ";" _lib_env "$ENV{LIB}") + else() + if(APPLE) + string(REPLACE ":" ";" _lib_env "$ENV{DYLD_LIBRARY_PATH}") + else() + string(REPLACE ":" ";" _lib_env "$ENV{LD_LIBRARY_PATH}") + endif() + list(APPEND _lib_env "${CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES}") + list(APPEND _lib_env "${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}") + endif() + endif() + list(REMOVE_DUPLICATES _lib_env) + + # Try to find the tmg lib in the given paths + # ---------------------------------------------- + + # call cmake macro to find the lib path + if(TMG_LIBDIR) + set(TMG_tmg_LIBRARY "TMG_tmg_LIBRARY-NOTFOUND") + find_library(TMG_tmg_LIBRARY + NAMES tmglib tmg + HINTS ${TMG_LIBDIR} ) + else() + if(TMG_DIR) + set(TMG_tmg_LIBRARY "TMG_tmg_LIBRARY-NOTFOUND") + find_library(TMG_tmg_LIBRARY + NAMES tmglib tmg + HINTS ${TMG_DIR} + PATH_SUFFIXES lib lib32 lib64 ) + else() + set(TMG_tmg_LIBRARY "TMG_tmg_LIBRARY-NOTFOUND") + find_library(TMG_tmg_LIBRARY + NAMES tmglib tmg + HINTS ${_lib_env} ) + endif() + endif() + mark_as_advanced(TMG_tmg_LIBRARY) + + # If found, add path to cmake variable + # ------------------------------------ + if (TMG_tmg_LIBRARY) + get_filename_component(tmg_lib_path ${TMG_tmg_LIBRARY} PATH) + # set cmake variables (respects naming convention) + set(TMG_LIBRARIES "${TMG_tmg_LIBRARY}") + set(TMG_LIBRARY_DIRS "${tmg_lib_path}") + else () + set(TMG_LIBRARIES "TMG_LIBRARIES-NOTFOUND") + set(TMG_LIBRARY_DIRS "TMG_LIBRARY_DIRS-NOTFOUND") + if(NOT TMG_FIND_QUIETLY) + message(STATUS "Looking for tmg -- lib tmg not found") + endif() + endif () + + if (TMG_LIBRARY_DIRS) + list(REMOVE_DUPLICATES TMG_LIBRARY_DIRS) + endif () + + # check a function to validate the find + if(TMG_LIBRARIES) + + set(REQUIRED_LDFLAGS) + set(REQUIRED_INCDIRS) + set(REQUIRED_LIBDIRS) + set(REQUIRED_LIBS) + + # TMG + if (TMG_INCLUDE_DIRS) + set(REQUIRED_INCDIRS "${TMG_INCLUDE_DIRS}") + endif() + if (TMG_LIBRARY_DIRS) + set(REQUIRED_LIBDIRS "${TMG_LIBRARY_DIRS}") + endif() + set(REQUIRED_LIBS "${TMG_LIBRARIES}") + # LAPACK + if (LAPACK_INCLUDE_DIRS) + list(APPEND REQUIRED_INCDIRS "${LAPACK_INCLUDE_DIRS}") + endif() + if (LAPACK_LIBRARY_DIRS) + list(APPEND REQUIRED_LIBDIRS "${LAPACK_LIBRARY_DIRS}") + endif() + list(APPEND REQUIRED_LIBS "${LAPACK_LIBRARIES}") + if (LAPACK_LINKER_FLAGS) + list(APPEND REQUIRED_LDFLAGS "${LAPACK_LINKER_FLAGS}") + endif() + + # set required libraries for link + set(CMAKE_REQUIRED_INCLUDES "${REQUIRED_INCDIRS}") + set(CMAKE_REQUIRED_LIBRARIES) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${REQUIRED_LDFLAGS}") + foreach(lib_dir ${REQUIRED_LIBDIRS}) + list(APPEND CMAKE_REQUIRED_LIBRARIES "-L${lib_dir}") + endforeach() + list(APPEND CMAKE_REQUIRED_LIBRARIES "${REQUIRED_LIBS}") + string(REGEX REPLACE "^ -" "-" CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") + + # test link + unset(TMG_WORKS CACHE) + include(CheckFunctionExists) + include(CheckFortranFunctionExists) + if (NOT _LANGUAGES_ MATCHES Fortran) + check_function_exists(dlarnv TMG_WORKS) + else (NOT _LANGUAGES_ MATCHES Fortran) + check_fortran_function_exists(dlarnv TMG_WORKS) + endif (NOT _LANGUAGES_ MATCHES Fortran) + if (TMG_WORKS) + unset(TMG_WORKS CACHE) + if (NOT _LANGUAGES_ MATCHES Fortran) + check_function_exists(dlagsy TMG_WORKS) + else (NOT _LANGUAGES_ MATCHES Fortran) + check_fortran_function_exists(dlagsy TMG_WORKS) + endif (NOT _LANGUAGES_ MATCHES Fortran) + mark_as_advanced(TMG_WORKS) + endif() + + if(TMG_WORKS) + # save link with dependencies + set(TMG_LIBRARIES_DEP "${REQUIRED_LIBS}") + set(TMG_LIBRARY_DIRS_DEP "${REQUIRED_LIBDIRS}") + set(TMG_INCLUDE_DIRS_DEP "${REQUIRED_INCDIRS}") + set(TMG_LINKER_FLAGS "${REQUIRED_LDFLAGS}") + list(REMOVE_DUPLICATES TMG_LIBRARY_DIRS_DEP) + list(REMOVE_DUPLICATES TMG_INCLUDE_DIRS_DEP) + list(REMOVE_DUPLICATES TMG_LINKER_FLAGS) + else() + if(NOT TMG_FIND_QUIETLY) + message(STATUS "Looking for tmg: test of dlarnv and dlagsy with tmg and lapack libraries fails") + message(STATUS "CMAKE_REQUIRED_LIBRARIES: ${CMAKE_REQUIRED_LIBRARIES}") + message(STATUS "CMAKE_REQUIRED_INCLUDES: ${CMAKE_REQUIRED_INCLUDES}") + message(STATUS "Check in CMakeFiles/CMakeError.log to figure out why it fails") + endif() + endif() + set(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_REQUIRED_FLAGS) + set(CMAKE_REQUIRED_LIBRARIES) + endif(TMG_LIBRARIES) + + endif() + +else() + + if(NOT TMG_FIND_QUIETLY) + message(STATUS "TMG requires LAPACK but LAPACK has not been found." + "Please look for LAPACK first.") + endif() + +endif() + +if (TMG_LIBRARIES) + list(GET TMG_LIBRARIES 0 first_lib) + get_filename_component(first_lib_path "${first_lib}" PATH) + if (${first_lib_path} MATCHES "(/lib(32|64)?$)|(/lib/intel64$|/lib/ia32$)") + string(REGEX REPLACE "(/lib(32|64)?$)|(/lib/intel64$|/lib/ia32$)" "" not_cached_dir "${first_lib_path}") + set(TMG_DIR_FOUND "${not_cached_dir}" CACHE PATH "Installation directory of TMG library" FORCE) + else() + set(TMG_DIR_FOUND "${first_lib_path}" CACHE PATH "Installation directory of TMG library" FORCE) + endif() +endif() +mark_as_advanced(TMG_DIR) +mark_as_advanced(TMG_DIR_FOUND) + +# check that TMG has been found +# ------------------------------- +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(TMG DEFAULT_MSG + TMG_LIBRARIES + TMG_WORKS) diff --git a/cmake/ImportBLAS.cmake b/cmake/ImportBLAS.cmake index 28a9c901..9bc2b502 100644 --- a/cmake/ImportBLAS.cmake +++ b/cmake/ImportBLAS.cmake @@ -8,16 +8,29 @@ # @author Mahmoud ElKarargy # @date 2023-03-12 -#Configurations +# Set basic configuration variables for the BLAS library. +# 'name' is set to "BLAS", which is the identifier used for the dependency throughout the script. set(name "BLAS") +# 'tag' specifies the version tag of the BLAS library to fetch, indicating a specific state of the source code in the repository. set(tag "v0.3.21") +# 'version' specifies the version of the BLAS library. This may be used to ensure compatibility or meet specific requirements. set(version "0.3.21") +# 'flag' can be used to pass additional flags to the configure/make commands when building the dependency, but it's empty here. set(flag "") +# 'is_cmake' is a boolean flag indicating whether the BLAS library uses CMake for its build system. It's set to ON, meaning it does. set(is_cmake ON) +# 'is_git' is a boolean flag indicating whether the BLAS library's source code is hosted in a git repository. It's set to ON. set(is_git ON) +# 'auto_gen' is a boolean flag indicating whether to use autogen scripts for the configuration process. It's set to OFF here. set(auto_gen OFF) +# 'url' specifies the location of the BLAS library's source code repository. set(url "https://github.com/xianyi/OpenBLAS") +# Include the 'ImportDependency' macro script located in the 'macros' directory. +# This macro is responsible for importing and possibly installing the dependency. include(macros/ImportDependency) +# Call the 'ImportDependency' macro with the previously set configuration parameters. +# This macro checks if BLAS is already available; if not, it proceeds to fetch, configure, build, and install it. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) +# Print a message indicating the completion of the BLAS setup process. message(STATUS "${name} done") diff --git a/cmake/ImportBLASPP.cmake b/cmake/ImportBLASPP.cmake index ef008506..6acea7c5 100644 --- a/cmake/ImportBLASPP.cmake +++ b/cmake/ImportBLASPP.cmake @@ -8,40 +8,58 @@ # @author Mahmoud ElKarargy # @date 2024-02-04 +# Include the ImportBLAS script, which presumably sets up basic BLAS dependencies. include(ImportBLAS) -#Configurations +# Set up basic configuration variables for locating or installing BLAS++. +# `name` variable set to 'blaspp' to represent the BLAS++ library throughout the script. set(name blaspp) +# Convert the name to uppercase and store it in `capital_name` for display purposes. string(TOUPPER ${name} capital_name) +# Set the `tag` variable to specify the version of BLAS++ to be used or installed. set(tag "v2023.01.00") +# Set the `url` variable to the location of the BLAS++ repository on GitHub. set(url "https://github.com/icl-utk-edu/blaspp") +# Configure the directory where BLAS++ will be installed or found. set(${name}_DIR "${CMAKE_INSTALL_PREFIX}/${capital_name}/${name}-build/") +# Display a header message indicating the start of the BLAS++ setup process. message("") message("---------------------------------------- ${capital_name}") +# Output the version of BLAS++ being checked for or installed. message(STATUS "Checking for ${capital_name} with Version ${version}") -# Check if the target is already included +# Check if the BLAS++ target is already defined in the current CMake environment. IF (NOT TARGET ${name}) + # Include the FindPkgConfig module to use pkg-config for finding installed libraries. include(FindPkgConfig) + # Attempt to find the pkg-config utility quietly. find_package(PkgConfig QUIET) + # Quietly search for the BLAS++ package, without specifying components or version. find_package(${name} ${version} QUIET COMPONENTS ${components}) - # If the package is found, print a message + # If BLAS++ is found, output its location and libraries linked. if (${name}_FOUND) message(" Found ${capital_name}; ${${name}_DIR} ${${name}_LIBRARIES}") else () - # If the package is not found, install it using BuildDependency + # If BLAS++ is not found, indicate it and proceed to install it. message(" Can't find ${capital_name}, Installing it instead ..") + # Include the FetchContent module to download external projects during the configure stage. include(FetchContent) + # Set the base directory for downloading and building the external project. set(FETCHCONTENT_BASE_DIR ${CMAKE_INSTALL_PREFIX}/${capital_name}) + # Declare the external project (BLAS++) with its Git repository and specific tag. FetchContent_Declare(${name} GIT_REPOSITORY "${url}" GIT_TAG "${tag}") + # Make the declared content available, effectively downloading and setting up BLAS++. FetchContent_MakeAvailable(${name}) endif () else () + # If the BLAS++ target is already defined, indicate that it's already included. message(STATUS "${capital_name} already included") endif () +# Add BLAS++ to the list of libraries to be linked against by setting `LIBS`. set(LIBS ${name} ${LIBS}) -message(STATUS "${name} done") \ No newline at end of file +# Final status message indicating completion of the BLAS++ setup. +message(STATUS "${name} done") diff --git a/cmake/ImportCatch2.cmake b/cmake/ImportCatch2.cmake index c00a6e29..0b6a7f9f 100644 --- a/cmake/ImportCatch2.cmake +++ b/cmake/ImportCatch2.cmake @@ -9,16 +9,26 @@ # @author Mahmoud ElKarargy # @date 2023-03-13 -#Configurations +# Sets the 'name' variable to "Catch2", identifying the dependency being imported. set(name "Catch2") +# Specifies the 'tag' variable as "v3.3.2", which likely corresponds to a specific release or version of Catch2. set(tag "v3.3.2") +# Sets the 'version' variable to "3.3.2", defining the expected version of Catch2 to be used in the project. set(version "3.3.2") +# Initializes the 'flag' variable as an empty string, which could be used for additional configuration options during the build or installation process. set(flag "") +# Sets the 'is_cmake' flag to ON, indicating that Catch2 uses CMake as its build system. set(is_cmake ON) +# Sets the 'is_git' flag to ON, suggesting that Catch2's source code is hosted in a git repository. set(is_git ON) +# Initializes the 'auto_gen' flag to OFF, implying that there's no need for auto-generation scripts (like autogen.sh) in the building process of Catch2. set(auto_gen OFF) +# Defines the 'url' variable with the GitHub repository URL of Catch2, specifying the source location from which Catch2 will be fetched. set(url "https://github.com/catchorg/Catch2.git") +# Includes the 'ImportDependency' macro script located in the 'macros' directory. This script is responsible for checking the presence of the specified dependency and importing it if necessary. include(macros/ImportDependency) +# Calls the 'ImportDependency' macro with the previously set variables as arguments. This macro will check for Catch2's presence, and if it's not found, it will attempt to fetch and install it using the provided details. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) +# Logs a message indicating the completion of the Catch2 import process. message(STATUS "${name} done") diff --git a/cmake/ImportChameleon.cmake b/cmake/ImportChameleon.cmake index b1d47819..1b2db765 100644 --- a/cmake/ImportChameleon.cmake +++ b/cmake/ImportChameleon.cmake @@ -10,20 +10,35 @@ # @author Sameh Abdulah # @date 2023-03-13 -#Configurations +# Set basic configuration variables for the Chameleon library. +# 'name' is set to "CHAMELEON", which is the identifier used for the dependency throughout the script. set(name "CHAMELEON") +# 'tag' specifies the version tag of the Chameleon library to fetch, indicating a specific state of the source code in the repository. set(tag "v1.1.0") +# 'version' specifies the version of the Chameleon library. This may be used to ensure compatibility or meet specific requirements. set(version "1.1.0") +# 'flag' can be used to pass additional flags to the configure/make commands when building the dependency. Flags here are for CUDA, BLAS vendor, MPI, StarPU, and disabling testing. set(flag -DCHAMELEON_USE_CUDA=${USE_CUDA} \-DBLA_VENDOR=${BLA_VENDOR} \-DCHAMELEON_USE_MPI=${USE_MPI} \-DCHAMELEON_SCHED_STARPU=ON \-DCHAMELEON_ENABLE_TESTING=OFF) +# 'is_cmake' is a boolean flag indicating whether the Chameleon library uses CMake for its build system. It's set to ON, meaning it does. set(is_cmake ON) +# 'is_git' is a boolean flag indicating whether the Chameleon library's source code is hosted in a git repository. It's set to ON. set(is_git ON) +# 'auto_gen' is a boolean flag indicating whether to use autogen scripts for the configuration process. It's set to OFF here. set(auto_gen OFF) +# 'url' specifies the location of the Chameleon library's source code repository. set(url "https://gitlab.inria.fr/solverstack/chameleon.git") +# Include the 'ImportDependency' macro script located in the 'macros' directory. +# This macro is responsible for importing and possibly installing the dependency. include(macros/ImportDependency) +# Call the 'ImportDependency' macro with the previously set configuration parameters. +# This macro checks if CHAMELEON is already available; if not, it proceeds to fetch, configure, build, and install it. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) +# Additional include directories for Chameleon are specified, ensuring the project can find Chameleon's headers. +# The AFTER keyword specifies that these directories should be searched after the default ones. include_directories(AFTER ${CHAMELEON_DIR_FOUND}/include/coreblas) include_directories(${CHAMELEON_DIR_FOUND}/chameleon-src) +# Print a status message indicating the completion of Chameleon's inclusion process. message(STATUS "${name} done") diff --git a/cmake/ImportGSL.cmake b/cmake/ImportGSL.cmake index a4010bab..016b0511 100644 --- a/cmake/ImportGSL.cmake +++ b/cmake/ImportGSL.cmake @@ -10,20 +10,32 @@ # @author Sameh Abdulah # @date 2023-03-16 -#Configurations +# Configurations for integrating the GSL (GNU Scientific Library) +# 'name' is assigned "GSL" to identify the GNU Scientific Library within this script. set(name "GSL") +# 'tag' specifies the version tag "v2.7.1" for the GSL library, indicating the exact version to be used. set(tag "v2.7.1") +# 'version' sets "2.7.1" as the version of the GSL library, ensuring compatibility with project requirements. set(version "2.7.1") +# 'flag' is available for additional configuration options during build or installation, but remains empty here. set(flag "") +# 'is_cmake' indicates whether GSL uses CMake for building. It is set to OFF, implying an alternative build system is used. set(is_cmake OFF) +# 'is_git' denotes if GSL's source code is hosted in a Git repository. It is set to OFF, suggesting the source is obtained from a different location. set(is_git OFF) +# 'auto_gen' signifies the need for autogen scripts in the build process. Here, it is set to OFF, indicating they are not needed. set(auto_gen OFF) +# 'url' provides the download location for the GSL source code, pointing to the GNU archive. set(url "https://ftp.gnu.org/gnu/gsl/gsl-2.7.1.tar.gz") +# Include the 'ImportDependency' macro, responsible for managing the GSL library's import and setup process. include(macros/ImportDependency) +# Execute the 'ImportDependency' macro with the previously established parameters to handle the detection, downloading, and integration of GSL. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) -# Add the GSL library to the project's list of libraries. +# Add GSL to the project's list of linked libraries, making its functionality accessible within the project. list(APPEND LIBS gsl) +# Output a message signaling the successful integration of the GSL library into the project. message(STATUS "${name} done") + diff --git a/cmake/ImportHCore.cmake b/cmake/ImportHCore.cmake index 42f26fa5..b5d51aad 100644 --- a/cmake/ImportHCore.cmake +++ b/cmake/ImportHCore.cmake @@ -10,17 +10,28 @@ # @author Sameh Abdulah # @date 2023-03-15 -#Configurations +# Configurations for the HCORE library +# 'name' is set to "HCORE", serving as the identifier for the dependency throughout this script. set(name "HCORE") +# 'tag' defines the version tag of the HCORE library to be fetched, representing a specific snapshot of the source code in the repository. set(tag "v0.1.3") +# 'version' indicates the version of the HCORE library, used to ensure compatibility or fulfill certain requirements. set(version "0.1.3") +# 'flag' is intended for passing additional flags to the configure/make commands during the building of the dependency, but is left empty here. set(flag "") +# 'is_cmake' is a boolean flag that denotes whether the HCORE library utilizes CMake for its build process. Here, it's set to ON. set(is_cmake ON) +# 'is_git' is a boolean flag that signifies if the source code for HCORE is maintained in a git repository. This is set to ON. set(is_git ON) +# 'auto_gen' is a boolean flag that indicates whether autogen scripts are required for the configuration process. It is set to OFF in this context. set(auto_gen OFF) +# 'url' provides the location of the HCORE library's source code repository. set(url "https://github.com/ecrc/hcore.git") +# The 'ImportDependency' macro script, located in the 'macros' directory, is included. This macro is crucial for importing and potentially installing the dependency. include(macros/ImportDependency) +# The 'ImportDependency' macro is invoked with the configuration parameters set above to handle the detection, fetching, and setup of HCORE. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) - +# A message is logged to indicate the successful completion of the HCORE setup process. message(STATUS "${name} done") + diff --git a/cmake/ImportHiCMA.cmake b/cmake/ImportHiCMA.cmake index e8b5e2ef..b6967f02 100644 --- a/cmake/ImportHiCMA.cmake +++ b/cmake/ImportHiCMA.cmake @@ -10,19 +10,31 @@ # @author Sameh Abdulah # @date 2023-03-13 -#Configurations +# Configuration settings for integrating the HICMA library into the project +# 'name' sets the identifier for the HICMA library within this script to "HICMA". set(name "HICMA") +# 'tag' specifies "v1.0.0" as the version tag of HICMA, denoting a specific release to be fetched. set(tag "v1.0.0") +# 'version' defines "1.0.0" as the version of the HICMA library, ensuring compatibility with project requirements. set(version "1.0.0") +# 'flag' is used to pass additional configuration options during the build, specifically for enabling or disabling MPI support based on the project's needs. set(flag -DHICMA_USE_MPI=${USE_MPI}) +# 'is_cmake' indicates that HICMA uses CMake for its build system, set to ON. set(is_cmake ON) +# 'is_git' denotes that HICMA's source code is hosted on a Git repository, set to ON. set(is_git ON) +# 'auto_gen' signals whether autogen scripts are necessary for the build process; it is set to OFF for HICMA. set(auto_gen OFF) +# 'url' provides the GitHub repository URL for HICMA, specifying where the source code can be cloned from. set(url "https://github.com/ecrc/hicma.git") +# The 'ImportDependency' macro, located in the 'macros' directory, is included. This macro handles the import and setup of the HICMA library. include(macros/ImportDependency) +# The 'ImportDependency' macro is invoked with the previously defined parameters to manage the detection, fetching, and setup of HICMA. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) -# Include HiCMA headers in the project. +# Directories containing HiCMA headers are included in the project, ensuring that HiCMA's functions and types are accessible. include_directories(${HICMA_LIBDIR}/../hicma-src/hicma_ext) +# A status message is displayed to indicate the successful inclusion of the HiCMA library into the project. message(STATUS "HiCMA done") + diff --git a/cmake/ImportHwloc.cmake b/cmake/ImportHwloc.cmake index c0f22bbc..04be165b 100644 --- a/cmake/ImportHwloc.cmake +++ b/cmake/ImportHwloc.cmake @@ -10,17 +10,29 @@ # @author Sameh Abdulah # @date 2023-03-15 -#Configurations +# Configuration settings for integrating the HWLOC library +# 'name' sets the identifier for the HWLOC library within this script to "HWLOC". set(name "HWLOC") -set(tag "hwloc-2.4.0") -set(version "2.4.0") +# 'tag' specifies "hwloc-2.10.0" as the version tag, identifying a specific release of HWLOC to be used. +set(tag "hwloc-2.10.0") +# 'version' defines "2.10.0" as the version of HWLOC, ensuring it meets project compatibility requirements. +set(version "2.10.0") +# 'flag' is available for additional build configuration options but remains empty for HWLOC. set(flag "") +# 'is_cmake' indicates whether HWLOC uses CMake for its build system. It's set to OFF, suggesting an alternative build system is used. set(is_cmake OFF) +# 'is_git' denotes that HWLOC's source code is maintained in a Git repository, set to ON. set(is_git ON) +# 'auto_gen' signals the need for running autogen scripts as part of the build process for HWLOC, set to ON. set(auto_gen ON) +# 'url' provides the GitHub repository URL for HWLOC, indicating the source code's location. set(url "https://github.com/open-mpi/hwloc") +# Includes the 'ImportDependency' macro script, located in the 'macros' directory, responsible for handling the import and setup of dependencies. include(macros/ImportDependency) +# The 'ImportDependency' macro is called with the configuration parameters above to manage the detection, fetching, and setup of HWLOC. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) +# A message is logged to indicate the successful integration of the HWLOC library into the project. message(STATUS "${name} done") + diff --git a/cmake/ImportLapack.cmake b/cmake/ImportLapack.cmake index 50586cd0..bb5517a4 100644 --- a/cmake/ImportLapack.cmake +++ b/cmake/ImportLapack.cmake @@ -10,17 +10,29 @@ # @author Sameh Abdulah # @date 2023-03-12 -#Configurations +# Configuration settings for the integration of the LAPACK library +# 'name' is designated as "LAPACK" to identify the LAPACK library within the scope of this script. set(name "LAPACK") +# 'tag' is set to "v0.3.21", specifying the particular version tag of LAPACK to be utilized. set(tag "v0.3.21") +# 'version' denotes the LAPACK library version as "0.3.21", aligned with the tag for consistency in versioning. set(version "0.3.21") +# 'flag' is intended for any additional flags needed for configuration or building, but is left blank in this case. set(flag "") +# 'is_cmake' is a boolean flag indicating that LAPACK uses CMake for its build process, set to ON. set(is_cmake ON) +# 'is_git' signifies that the source code for LAPACK is available in a Git repository, set to ON. set(is_git ON) +# 'auto_gen' indicates whether autogen scripts are necessary for the configuration process; it is set to OFF for LAPACK. set(auto_gen OFF) +# 'url' provides the repository URL for LAPACK, pointing to the location where the source code can be accessed. set(url "https://github.com/xianyi/OpenBLAS") +# The 'ImportDependency' macro script from the 'macros' directory is included, which facilitates the import and setup of dependencies. include(macros/ImportDependency) +# The 'ImportDependency' macro is executed with the above-defined parameters to manage the detection, retrieval, and configuration of LAPACK. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) +# A status message is output to indicate the successful completion of the LAPACK setup process. message(STATUS "${name} done") + diff --git a/cmake/ImportNLOPT.cmake b/cmake/ImportNLOPT.cmake index c99add7f..f82c786b 100644 --- a/cmake/ImportNLOPT.cmake +++ b/cmake/ImportNLOPT.cmake @@ -10,17 +10,29 @@ # @author Sameh Abdulah # @date 2023-03-26 -#Configurations +# Configuration settings for the integration of the NLOPT library +# 'name' is assigned to "NLOPT", serving as the identifier for this library within the script. set(name "NLOPT") +# 'tag' defines "v2.7.1" as the version tag of NLOPT, indicating the specific release to be utilized. set(tag "v2.7.1") +# 'version' specifies "2.7.1" as the version of the NLOPT library, ensuring compatibility with the project's requirements. set(version "2.7.1") +# 'flag' is intended for additional configuration options during the build process. A space is placed as a placeholder. set(flag " ") +# 'is_cmake' indicates that NLOPT uses CMake for its build system, which is set to ON. set(is_cmake ON) +# 'is_git' denotes that the NLOPT source code is hosted in a Git repository, which is set to ON. set(is_git ON) +# 'auto_gen' signals whether autogen scripts are required for the build process, which is set to OFF for NLOPT. set(auto_gen OFF) +# 'url' provides the location of the NLOPT source code repository on GitHub. set(url "https://github.com/stevengj/nlopt") +# The 'ImportDependency' macro script, located in the 'macros' directory, is included for managing the import and setup of the NLOPT library. include(macros/ImportDependency) +# The 'ImportDependency' macro is invoked with the above-defined parameters to handle the detection, fetching, and integration of NLOPT into the project. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) +# A status message is outputted to indicate the successful integration of the NLOPT library into the project. message(STATUS "${name} done") + diff --git a/cmake/ImportStarPu.cmake b/cmake/ImportStarPu.cmake index 24dbb773..f51f0a42 100644 --- a/cmake/ImportStarPu.cmake +++ b/cmake/ImportStarPu.cmake @@ -10,27 +10,43 @@ # @author Sameh Abdulah # @date 2023-03-13 -#Configurations +# Configuration settings for integrating the STARPU library into the project +# 'name' is set to "STARPU" to identify this specific library within the script. set(name "STARPU") -set(tag "starpu-1.3.9") -set(version "1.3.9") +# 'tag' specifies "starpu-1.3.10" as the version tag, indicating the exact version of STARPU to be used. +set(tag "starpu-1.3.10") +# 'version' sets "1.3.10" as the version of the STARPU library, ensuring project compatibility. +set(version "1.3.10") +# Conditional setting of 'flag' based on project configurations for CUDA and MPI. if (USE_CUDA AND USE_MPI) + # Sets flags for enabling CUDA and MPI, and disables OpenCL, documentation build, and export dynamic when both CUDA and MPI are used. set(flag \--enable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--enable-mpi) elseif(USE_CUDA) + # Sets flags for enabling CUDA and shared libraries, and disables OpenCL, documentation build, export dynamic, and MPI when only CUDA is used. set(flag \--enable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--disable-mpi) elseif(USE_MPI) + # Sets flags for enabling MPI and shared libraries, and disables CUDA, OpenCL, documentation build, and export dynamic when only MPI is used. set(flag \--disable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--enable-mpi) else() + # Sets flags for enabling shared libraries, and disables CUDA, OpenCL, documentation build, export dynamic, and MPI when neither CUDA nor MPI is used. set(flag \--disable-cuda \--disable-opencl \--enable-shared \--disable-build-doc \--disable-export-dynamic \--disable-mpi) endif() +# 'is_cmake' is set to OFF indicating STARPU does not use CMake as its primary build system. set(is_cmake OFF) +# 'is_git' is set to ON, denoting that STARPU's source code is maintained in a Git repository. set(is_git ON) +# 'auto_gen' is set to ON, signaling the need for autogen scripts to be run as part of the build process. set(auto_gen ON) +# 'url' provides the location of the STARPU source code repository. set(url "https://gitlab.inria.fr/starpu/starpu.git") +# Include the 'ImportDependency' macro script, responsible for handling the import and setup of dependencies. include(macros/ImportDependency) +# The 'ImportDependency' macro is invoked with the configuration parameters to manage the detection, fetching, and integration of STARPU. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "${STARPU_COMPONENT_LIST}" ${is_cmake} ${is_git} ${auto_gen}) +# A message is logged to indicate the successful integration of the STARPU library into the project. message(STATUS "${name} done") + diff --git a/cmake/ImportStarsH.cmake b/cmake/ImportStarsH.cmake index c0c1ce93..35cbeab7 100644 --- a/cmake/ImportStarsH.cmake +++ b/cmake/ImportStarsH.cmake @@ -10,17 +10,28 @@ # @author Sameh Abdulah # @date 2023-03-13 -#Configurations +# Configuration parameters for integrating the STARSH library +# 'name' is set to "STARSH" to identify the STARSH library within this script. set(name "STARSH") +# 'tag' specifies "v0.3.1" as the version tag for STARSH, denoting the exact release to be used. set(tag "v0.3.1") +# 'version' sets "0.3.1" as the version of the STARSH library, ensuring it aligns with project requirements. set(version "0.3.1") +# 'flag' is used for additional build configuration options, specifically disabling StarPU and optionally enabling MPI. set(flag \-DSTARPU=OFF \-DMPI=${USE_MPI}) +# 'is_cmake' indicates that STARSH uses CMake as its build system, set to ON. set(is_cmake ON) +# 'is_git' denotes that the source code for STARSH is hosted on a Git repository, set to ON. set(is_git ON) +# 'auto_gen' signals whether autogen scripts are needed for the build process; it is set to OFF for STARSH. set(auto_gen OFF) +# 'url' provides the GitHub repository URL for STARSH, specifying the source code's location. set(url "https://github.com/ecrc/stars-h.git") +# The 'ImportDependency' macro, located in the 'macros' directory, is included to manage the import and setup of the STARSH library. include(macros/ImportDependency) +# The 'ImportDependency' macro is called with the configuration parameters set above to manage the detection, fetching, and setup of STARSH. ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_git} ${auto_gen}) +# A message is output to indicate the successful integration of the STARSH library into the project. message(STATUS "${name} done") diff --git a/cmake/macros/BuildDependency.cmake b/cmake/macros/BuildDependency.cmake index bc112db9..483dc05d 100644 --- a/cmake/macros/BuildDependency.cmake +++ b/cmake/macros/BuildDependency.cmake @@ -28,72 +28,82 @@ # CMake or autotools, and finally, it's built and installed. Environment variables are set, and the dependency's # lib, include, and share directories are installed in the current directory. +# Define the macro BuildDependency with parameters for handling various aspects of dependency management. macro(BuildDependency raw_name url tag flags is_using_cmake is_using_git auto_generation) - # Set the name of the dependency. + # Convert the raw dependency name to lowercase and uppercase for different uses and set them as 'name' and 'capital_name'. string(TOLOWER ${raw_name} name) string(TOUPPER ${raw_name} capital_name) - # Fetch the dependency, depending on whether it's a git repo or not. + # Log the start of the fetch process for the dependency, including its name, tag, and source URL. message(STATUS "Fetching ${name} ${tag} from ${url}") + # Include the CMake module for downloading and updating content during the configure step. include(FetchContent) + # Set the base directory for fetched content to a directory within the install prefix, named after the dependency. set(FETCHCONTENT_BASE_DIR ${CMAKE_INSTALL_PREFIX}/${capital_name}) + # Check if the dependency is hosted in a git repository and declare it accordingly with FetchContent, using git-specific options. if (${is_using_git}) FetchContent_Declare(${name} GIT_REPOSITORY "${url}" GIT_TAG "${tag}" - GIT_SHALLOW TRUE - GIT_PROGRESS TRUE + GIT_SHALLOW TRUE # For a shallow clone, fetching only the history needed for the specified tag + GIT_PROGRESS TRUE # Show progress during the clone ) else () + # If not using git, declare the dependency for FetchContent using a direct URL (e.g., for a tarball). FetchContent_Declare(${name} URL "${url}") endif () + # Make the content available, effectively downloading it if necessary. FetchContent_Populate(${name}) - # Set up build paths and create a directory for build artifacts. + # Set variables for the source path, binary (build) path, and installation path of the dependency. set(${name}_srcpath ${CMAKE_INSTALL_PREFIX}/${capital_name}/${name}-src) set(${name}_binpath ${${name}_srcpath}/bin) set(${name}_installpath ${CMAKE_INSTALL_PREFIX}/${capital_name}) + # Ensure the binary path directory exists. file(MAKE_DIRECTORY ${${name}_binpath}) - # Configure subproject. + # Configure the project. If using CMake, run cmake command with specified flags and install prefix within the binary path. if (${is_using_cmake}) execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/${capital_name} -DCMAKE_C_FLAGS=-fPIC ${flags} ${${name}_srcpath} WORKING_DIRECTORY ${${name}_binpath}) else () + # For non-CMake projects, run autogen.sh if auto_generation is true, then configure the project with specified flags. if (${auto_generation}) execute_process(COMMAND ./autogen.sh WORKING_DIRECTORY ${${name}_srcpath} - COMMAND_ERROR_IS_FATAL ANY) + COMMAND_ERROR_IS_FATAL ANY) # Halt on error endif () execute_process(COMMAND ./configure --prefix=${CMAKE_INSTALL_PREFIX}/${capital_name} ${flags} WORKING_DIRECTORY ${${name}_srcpath} - COMMAND_ERROR_IS_FATAL ANY) + COMMAND_ERROR_IS_FATAL ANY) # Halt on error endif () - # Build and install subproject. + # Include the ProcessorCount module to determine the number of CPUs for parallel build and install commands. include(ProcessorCount) ProcessorCount(N) + # Build the project using make, with parallel jobs based on processor count. This applies to both CMake and non-CMake projects. if (${is_using_cmake}) execute_process(COMMAND make -j ${N} WORKING_DIRECTORY ${${name}_binpath} - COMMAND_ERROR_IS_FATAL ANY) + COMMAND_ERROR_IS_FATAL ANY) # Halt on error + # Install the built project, also with parallel jobs. execute_process(COMMAND make install -j ${N} WORKING_DIRECTORY ${${name}_binpath} - COMMAND_ERROR_IS_FATAL ANY) + COMMAND_ERROR_IS_FATAL ANY) # Halt on error else () execute_process(COMMAND make -j ${N} WORKING_DIRECTORY ${${name}_srcpath} - COMMAND_ERROR_IS_FATAL ANY) + COMMAND_ERROR_IS_FATAL ANY) # Halt on error execute_process(COMMAND make install -j ${N} WORKING_DIRECTORY ${${name}_srcpath} - COMMAND_ERROR_IS_FATAL ANY) + COMMAND_ERROR_IS_FATAL ANY) # Halt on error endif () - # Set environment variables and include/link to the installation directory of the dependency. + # Set environment variables for dynamic and static linking as well as include paths, pointing to the dependency's installation directory. set(ENV{LD_LIBRARY_PATH} "${${name}_installpath}/lib:${${name}_installpath}/lib64:$ENV{LD_LIBRARY_PATH}") set(ENV{LIBRARY_PATH} "${${name}_installpath}/lib:${${name}_installpath}/lib64:$ENV{LIBRARY_PATH}") set(ENV{CPATH} "${${name}_installpath}/include:$ENV{CPATH}") diff --git a/cmake/macros/ImportDependency.cmake b/cmake/macros/ImportDependency.cmake index b32580dd..806da212 100644 --- a/cmake/macros/ImportDependency.cmake +++ b/cmake/macros/ImportDependency.cmake @@ -25,49 +25,61 @@ # If the package is found, it prints a message. If not, it calls the BuildDependency macro to fetch, # configure, build, and install the dependency. Finally, it attempts to find the package again to validate the installation. +# Define a macro named ImportDependency for handling external dependencies. The macro checks for the dependency's presence and installs it if missing. macro(ImportDependency name tag version url flag components is_cmake is_git auto_gen) - # First, Check if no path is set for installation. + # Check if the installation prefix is set to a system path (like /usr/) and warn the user about potential need for administrative privileges. if (CMAKE_INSTALL_PREFIX MATCHES "/usr/") message(WARNING "Installation path not specified. Please set the installation path using -DCMAKE_INSTALL_PREFIX=path/to/install or execute ./config.sh. Otherwise, please note that administrative privileges may be required to install in system paths.") endif () - # Convert name to uppercase for consistency + # Convert the dependency name to uppercase for consistent messaging. string(TOUPPER ${name} capital_name) + # Begin a section in the output to visually separate the handling of this dependency. message("") message("---------------------------------------- ${capital_name}") + # Log the attempt to check for the specified version of the dependency. message(STATUS "Checking for ${capital_name} with Version ${version}") - # Include the BuildDependency macro + # Include the previously defined BuildDependency macro script for potential use. include(macros/BuildDependency) - # Check if the target is already included + # If the dependency has not already been targeted for building in the current CMake process, proceed to check its presence. IF (NOT TARGET ${name}) + # Use the FindPkgConfig module to potentially use pkg-config for finding installed libraries. include(FindPkgConfig) find_package(PkgConfig QUIET) + # Attempt to find the specified version of the package quietly, without generating much output. find_package(${name} ${version} QUIET COMPONENTS ${components}) - # If the package is found, print a message + # If the package is found, output a message detailing the found configuration. if (${name}_FOUND) message(" Found ${capital_name}; ${${name}_DIR} ${${name}_LIBRARIES}") else () - # If the package is not found, install it using BuildDependency + # If the package is not found, notify and invoke BuildDependency to install it. message(" Can't find ${capital_name}, Installing it instead ..") BuildDependency(${name} ${url} ${tag} "${flag}" ${is_cmake} ${is_git} ${auto_gen}) + # After attempting installation, forcibly attempt to find the package again, this time requiring its presence. find_package(${name} ${version} REQUIRED COMPONENTS ${components}) endif () else () + # If the dependency target already exists, log that it's already been included. message(STATUS "${capital_name} already included") endif () - # Include and link to the installation directory of the dependency + # Setup link and include directories based on the found or installed package configuration. + # Add the dependency's library directories to the link directories for the current CMake target. link_directories(${${name}_LIBRARY_DIRS_DEP}) link_directories(${${name}_LIBRARY_DIRS}) + # Add the dependency's include directories to the include path. include_directories(${${name}_INCLUDE_DIRS}) include_directories(AFTER ${${name}_INCLUDE_DIRS_DEP}) + + # If the dependency is not GSL, append its libraries to the list of libraries to be linked against. if(NOT ${name} STREQUAL "GSL") list(APPEND LIBS ${${name}_LIBRARIES}) endif() + # Append any additional dependency libraries to the list of libraries. list(APPEND LIBS ${${name}_LIBRARIES_DEP}) -endmacro() \ No newline at end of file +endmacro() diff --git a/configure b/configure index fb4b2e58..772a9ed0 100755 --- a/configure +++ b/configure @@ -3,7 +3,7 @@ # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). -# @file config.sh +# @file configure # @version 1.1.0 # @author Mahmoud ElKarargy # @author David Helmy @@ -56,7 +56,6 @@ else ABSOLUTE_PATH=$(dirname "$(realpath "$0")") fi - BUILDING_TESTS="OFF" BUILDING_HEAVY_TESTS="OFF" BUILDING_EXAMPLES="OFF" @@ -66,6 +65,7 @@ USE_CUDA="OFF" USE_MPI="OFF" BLAS_VENDOR="" PACKAGE="OFF" +RUNTIME_TYPE="starpu" SHOW_WARNINGS="OFF" COMPILE_FLAGS="-Wl,--no-as-needed -w -fpic" DEVELOPER_WARNINGS="-Wno-dev" @@ -76,11 +76,21 @@ do --use-mkl) echo "${GREEN}MKL as a BLA vendor${NC}" BLAS_VENDOR="Intel10_64lp" - shift # Remove --use-mkl from processing + ;; + --use-parsec) + echo "${GREEN}Parsec as a runtime${NC}" + RUNTIME_TYPE="parsec" + ;; + *) + # Collect non-option arguments as a space-delimited string + params="$params $arg" ;; esac done +# Reset positional parameters to collected non-option arguments +eval set -- $params + # Parse command line options while getopts ":tevhHi:cmpTwr" opt; do case $opt in @@ -139,6 +149,8 @@ while getopts ":tevhHi:cmpTwr" opt; do h) ##### Prints the help ##### echo "Usage of $(basename "$0"):" echo "" + printf "%20s %s\n" "--use-mkl :" "to use MKL as a BLA vendor." + printf "%20s %s\n" "--use-parsec :" "to use parsec runtime." printf "%20s %s\n" "-i [path] :" "specify installation path, default = ${PWD}/installdir/_deps/" printf "%20s %s\n" "-t :" "to enable building tests." printf "%20s %s\n" "-T :" "to enable building heavy tests." @@ -147,10 +159,8 @@ while getopts ":tevhHi:cmpTwr" opt; do printf "%20s %s\n" "-c :" "to enable using CUDA." printf "%20s %s\n" "-m :" "to enable using MPI." printf "%20s %s\n" "-v :" "to enable verbose printings." - printf "%20s %s\n" "-d :" "to enable debug mode." - printf "%20s %s\n" "-r :" "to enable Rcpp support." - printf "%20s %s\n" "--use-mkl :" "to force the use of mkl as your bla vendor." printf "%20s %s\n" "-p :" "to enable a packaging system for distribution." + printf "%20s %s\n" "-w :" "to enable showing warnings." printf "%20s %s\n" "-h :" "Help." echo "" exit 1 @@ -168,8 +178,6 @@ if [ -z "$BUILDING_EXAMPLES" ]; then echo "${RED}Building examples disabled.${NC}" fi -echo "${BLUE}Installation path set to $INSTALL_PREFIX.${NC}" - if [ -z "$USING_HiCMA" ]; then echo "${RED}Using HiCMA is disabled.${NC}" fi @@ -196,6 +204,7 @@ if [ -z "$USE_R" ]; then echo "${RED}Using R is disabled${NC}" fi +echo "${BLUE}Installation path set to $INSTALL_PREFIX.${NC}" echo "" echo "${YELLOW}Use -h to print the usages of exageostat-cpp flags.${NC}" echo "" @@ -235,6 +244,7 @@ fi -DUSE_R="${USE_R}" \ -DBLA_VENDOR="${BLAS_VENDOR}" \ -DCREATE_PACKAGE="${PACKAGE}" \ + -DRUNTIME_TYPE="${RUNTIME_TYPE}" \ -DBUILD_SHARED_LIBS=OFF \ -H"${ABSOLUTE_PATH}" \ -B"${ABSOLUTE_PATH}/bin" \ @@ -250,7 +260,7 @@ if [ "$USE_R" = "ON" ]; then } # Clean the directory and build the code with the specified options. - "$cmake_command_bin" --build . -j 6 + "$cmake_command_bin" --build . -j 10 if [ "$OS_TYPE" = "darwin"* ]; then cp "${ABSOLUTE_PATH}/bin/src/libExaGeoStatCPP.dylib" "${ABSOLUTE_PATH}/src/ExaGeoStatCPP.so" || echo "Failed: libExaGeoStatCPP.dylib -> src" diff --git a/examples/configurations/CMakeLists.txt b/examples/configurations/CMakeLists.txt index a3a22fc9..c1c09aa1 100644 --- a/examples/configurations/CMakeLists.txt +++ b/examples/configurations/CMakeLists.txt @@ -4,7 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt -# @brief Defines an executable and links it with the ExaGeoStat library and other libraries. +# @brief Defines an executables and links them with the ExaGeoStat library and other libraries. # @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-31 diff --git a/examples/configurations/RunningWithDifferentConfigurations.cpp b/examples/configurations/RunningWithDifferentConfigurations.cpp index 5962adae..4e85cc4b 100644 --- a/examples/configurations/RunningWithDifferentConfigurations.cpp +++ b/examples/configurations/RunningWithDifferentConfigurations.cpp @@ -15,12 +15,14 @@ #include using namespace exageostat::api; -using namespace exageostat::dataunits; +using namespace exageostat::configurations; /** * @brief Main entry point for the Data Generation & Data Modeling program. - * @details - * @return An integer indicating the success or failure of the program. + * @details Initializes configuration settings, loads data, and performs data modeling with ExaGeoStat. + * The program demonstrates running ExaGeoStat with two different sets of configurations to showcase the software's capability in handling various statistical models and computational settings. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * */ int main() { @@ -46,9 +48,9 @@ int main() { configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. std::unique_ptr> data; - ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); + ExaGeoStat::ExaGeoStatLoadData(configurations, data); // Modeling module. - ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); + ExaGeoStat::ExaGeoStatDataModeling(configurations, data); LOGGER("") LOGGER("ANOTHER CONFIGURATIONS\n") @@ -65,9 +67,9 @@ int main() { configurations2.SetInitialTheta(initial_theta); // Load data by either read from file or create synthetic data. - ExaGeoStat::ExaGeoStatLoadData(hardware, configurations2, data); + ExaGeoStat::ExaGeoStatLoadData(configurations2, data); // Modeling module. - ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations2, data); + ExaGeoStat::ExaGeoStatDataModeling(configurations2, data); return 0; } diff --git a/examples/configurations/SetupConfigurations.cpp b/examples/configurations/SetupConfigurations.cpp index a06ca247..d91bbea0 100644 --- a/examples/configurations/SetupConfigurations.cpp +++ b/examples/configurations/SetupConfigurations.cpp @@ -14,24 +14,22 @@ * **/ -#include - #include #include using namespace std; using namespace exageostat::common; +using namespace exageostat::configurations; /** * @brief The main function of the program. - * - * This function demonstrates how to use the Configurations class from the ExaGeoStat software package + * @details This example demonstrates how to use the Configurations class from the ExaGeoStat software package * to obtain user-defined configurations. - * * @param[in] argc The number of command line arguments. * @param[in] argv The command line arguments. - * @return The status code of the program. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * */ int main(int argc, char **argv) { diff --git a/examples/data-generators/SyntheticDataGeneration.cpp b/examples/data-generators/SyntheticDataGeneration.cpp index 214499f0..afa3c6a5 100644 --- a/examples/data-generators/SyntheticDataGeneration.cpp +++ b/examples/data-generators/SyntheticDataGeneration.cpp @@ -11,22 +11,20 @@ * @date 2024-02-04 **/ -#include - #include -using namespace std; - using namespace exageostat::generators; using namespace exageostat::common; using namespace exageostat::kernels; +using namespace exageostat::configurations; /** * @brief The main function of the program. * @details This function generates synthetic data for ExaGeoStat using the provided command line arguments. * @param[in] argc The number of command line arguments. * @param[in] argv The command line arguments. - * @return The status code of the program. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * */ int main(int argc, char **argv) { @@ -45,11 +43,11 @@ int main(int argc, char **argv) { synthetic_data_configurations.GetKernelName(), synthetic_data_configurations.GetTimeSlot()); // Create a unique pointer to a DataGenerator object - unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( + std::unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( synthetic_data_configurations); // Initialize the locations of the generated data - auto data = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); + auto data = synthetic_generator->CreateData(synthetic_data_configurations, *pKernel); // Define a struct to hold pointers to the x, y, and z coordinates of the generated data struct DataPointers { double *x; @@ -81,6 +79,5 @@ int main(int argc, char **argv) { } delete pKernel; - return 0; } \ No newline at end of file diff --git a/examples/data-loader/CMakeLists.txt b/examples/data-loader/CMakeLists.txt index 2268a81b..007661d8 100644 --- a/examples/data-loader/CMakeLists.txt +++ b/examples/data-loader/CMakeLists.txt @@ -4,6 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt +# @brief Defines an executables and link it with the ExaGeoStat library and other libraries. # @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-02-14 diff --git a/examples/data-loader/CSVLoader.cpp b/examples/data-loader/CSVLoader.cpp index 8cd90dcb..0f04119c 100644 --- a/examples/data-loader/CSVLoader.cpp +++ b/examples/data-loader/CSVLoader.cpp @@ -12,16 +12,26 @@ **/ #include -#include using namespace std; using namespace exageostat::kernels; -using namespace exageostat::common; using namespace exageostat::generators; using namespace exageostat::dataLoader::csv; +using namespace exageostat::configurations; + +/** + * @brief Main entry point demonstrating the usage of CSVLoader in ExaGeoStat. + * @details The program demonstrates initializing configuration settings, creating synthetic data, setting up ExaGeoStat hardware, initializing kernels, and then using the + * CSVLoader to read spatial and measurement data from a CSV file. The spatial dimensions can be 2D or 3D, and the data read includes locations (X, Y, [Z]) and measurements. + * @param argc The number of command-line arguments. + * @param argv The array of command-line arguments. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * + */ int main(int argc, char **argv) { + LOGGER("** Example of CSV Loader **") // Create and Initialize a new configurations object. @@ -43,7 +53,7 @@ int main(int argc, char **argv) { // Create a unique pointer to a DataGenerator object and generate data configurations.SetIsSynthetic(true); unique_ptr> synthetic_generator = DataGenerator::CreateGenerator(configurations); - auto data = synthetic_generator->CreateData(configurations, hardware, *kernel); + auto data = synthetic_generator->CreateData(configurations, *kernel); // Read csv file using data loader vector measurements_vector; @@ -58,11 +68,12 @@ int main(int argc, char **argv) { LOGGER("Data Loaded:") for (int i=0;i #include -#include -#include #include using namespace std; using namespace exageostat::common; using namespace exageostat::kernels; -using namespace exageostat::plugins; using namespace exageostat::dataunits; +using namespace exageostat::configurations; +/** + * @brief Main entry point for demonstrating the use of Chameleon Descriptor in ExaGeoStat. + * @details Initializes configurations, hardware, data, and kernels. Demonstrates the setup and usage of a Chameleon descriptor for handling matrices, including setting up matrix + * dimensions, tile sizes, distribution grids, and memory management. The example showcases how to interact with the descriptor to access and manage data efficiently for computational purposes. + * @param argc Number of command-line arguments. + * @param argv Array of command-line argument strings. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * + */ int main(int argc, char **argv) { LOGGER("** Example of Chameleon Descriptor **") @@ -36,7 +43,7 @@ int main(int argc, char **argv) { auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), configuration.GetGPUsNumbers()); unique_ptr> data = make_unique>(); - Kernel *kernel = PluginRegistry>::Create( + Kernel *kernel = exageostat::plugins::PluginRegistry>::Create( configuration.GetKernelName(), configuration.GetTimeSlot()); // Get arguments for Descriptors Initialization @@ -90,4 +97,5 @@ int main(int argc, char **argv) { } delete kernel; + return 0; } diff --git a/examples/descriptors/ChameleonToHicmaConverter.cpp b/examples/descriptors/ChameleonToHicmaConverter.cpp index 299b9e89..624e131b 100644 --- a/examples/descriptors/ChameleonToHicmaConverter.cpp +++ b/examples/descriptors/ChameleonToHicmaConverter.cpp @@ -13,16 +13,25 @@ #include #include -#include -#include #include using namespace std; using namespace exageostat::common; using namespace exageostat::kernels; -using namespace exageostat::plugins; using namespace exageostat::dataunits; +using namespace exageostat::configurations; + +/** + * @brief Main entry point for the Chameleon to Hicma descriptor conversion example. + * @details Demonstrates the process of initializing ExaGeoStat configurations, setting up hardware, creating and initializing a kernel, and then generating and setting up a + * Chameleon descriptor. The example then showcases the conversion of the Chameleon descriptor to a Hicma descriptor, detailing the changes and continuity in descriptor + * attributes and matrix data through the conversion process. + * @param argc Number of command-line arguments. + * @param argv Array of command-line argument strings. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * + */ int main(int argc, char **argv) { @@ -36,7 +45,7 @@ int main(int argc, char **argv) { auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), configuration.GetGPUsNumbers()); unique_ptr> data = make_unique>(); - Kernel *kernel = PluginRegistry>::Create( + Kernel *kernel = exageostat::plugins::PluginRegistry>::Create( configuration.GetKernelName(), configuration.GetTimeSlot()); // Get arguments for Descriptors Initialization @@ -117,4 +126,5 @@ int main(int argc, char **argv) { delete kernel; delete HICMA_descriptorC; + return 0; } diff --git a/examples/descriptors/HicmaDescriptor.cpp b/examples/descriptors/HicmaDescriptor.cpp index 395b795a..ce38a255 100644 --- a/examples/descriptors/HicmaDescriptor.cpp +++ b/examples/descriptors/HicmaDescriptor.cpp @@ -13,16 +13,25 @@ #include #include -#include -#include #include using namespace std; using namespace exageostat::common; using namespace exageostat::kernels; -using namespace exageostat::plugins; using namespace exageostat::dataunits; +using namespace exageostat::configurations; + +/** + * @brief Main entry point for the Hicma descriptor example in ExaGeoStat. + * @details Initializes configurations tailored for Hicma usage, sets up hardware, creates a kernel, and demonstrates the creation and initialization of a Hicma descriptor. + * The process includes checking for TILE_LOW_RANK computation mode, generating a data matrix, and setting up the Hicma descriptor with this matrix, showcasing how ExaGeoStat + * handles hierarchical matrix representations for efficient large-scale computations. + * @param argc Number of command-line arguments. + * @param argv Array of command-line argument strings. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * + */ int main(int argc, char **argv) { @@ -42,7 +51,7 @@ int main(int argc, char **argv) { auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), configuration.GetGPUsNumbers()); unique_ptr> data = make_unique>(); - Kernel *kernel = PluginRegistry>::Create( + Kernel *kernel = exageostat::plugins::PluginRegistry>::Create( configuration.GetKernelName(), configuration.GetTimeSlot()); // Get arguments for Descriptors Initialization @@ -95,4 +104,5 @@ int main(int argc, char **argv) { } delete kernel; + return 0; } diff --git a/examples/end-to-end/CMakeLists.txt b/examples/end-to-end/CMakeLists.txt index e4020a01..a4b0f472 100644 --- a/examples/end-to-end/CMakeLists.txt +++ b/examples/end-to-end/CMakeLists.txt @@ -4,6 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt +# @brief Defines an executables and links them with the ExaGeoStat library and other libraries. # @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-01-31 diff --git a/examples/end-to-end/DataGeneration.cpp b/examples/end-to-end/DataGeneration.cpp index f7c22d0a..f392be6e 100644 --- a/examples/end-to-end/DataGeneration.cpp +++ b/examples/end-to-end/DataGeneration.cpp @@ -12,18 +12,17 @@ * @date 2024-02-04 **/ -#include #include -using namespace exageostat::api; -using namespace exageostat::dataunits; +using namespace exageostat::configurations; /** * @brief Main entry point for the DataGeneration program. * @details This function either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data. * @param[in] argc The number of command line arguments. * @param[in] argv An array of command line argument strings. - * @return An integer indicating the success or failure of the program. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * */ int main(int argc, char **argv) { @@ -36,7 +35,7 @@ int main(int argc, char **argv) { configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. std::unique_ptr> data; - ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(configurations, data); return 0; } \ No newline at end of file diff --git a/examples/end-to-end/DataGenerationAndModeling.cpp b/examples/end-to-end/DataGenerationAndModeling.cpp index dd461e74..092f9bcb 100644 --- a/examples/end-to-end/DataGenerationAndModeling.cpp +++ b/examples/end-to-end/DataGenerationAndModeling.cpp @@ -12,18 +12,18 @@ * @date 2024-02-04 **/ -#include #include using namespace exageostat::api; -using namespace exageostat::dataunits; +using namespace exageostat::configurations; /** * @brief Main entry point for the Data Generation & Data Modeling program. * @details This function either generates synthetic data using the ExaGeoStat library, or reads an CSV file, then models the loaded data. * @param[in] argc The number of command line arguments. * @param[in] argv An array of command line argument strings. - * @return An integer indicating the success or failure of the program. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * */ int main(int argc, char **argv) { @@ -36,9 +36,9 @@ int main(int argc, char **argv) { configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. std::unique_ptr> data; - ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); + ExaGeoStat::ExaGeoStatLoadData(configurations, data); // Modeling module. - ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); + ExaGeoStat::ExaGeoStatDataModeling(configurations, data); return 0; } diff --git a/examples/end-to-end/DataGenerationAndPrediction.cpp b/examples/end-to-end/DataGenerationAndPrediction.cpp index aace2c7a..4845dcfc 100644 --- a/examples/end-to-end/DataGenerationAndPrediction.cpp +++ b/examples/end-to-end/DataGenerationAndPrediction.cpp @@ -12,18 +12,18 @@ * @date 2024-03-03 **/ -#include #include using namespace exageostat::api; -using namespace exageostat::dataunits; +using namespace exageostat::configurations; /** * @brief Main entry point for the Data Generation & Data Modeling program. * @details This function either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data, models it, and predicts missing values. * @param[in] argc The number of command line arguments. * @param[in] argv An array of command line argument strings. - * @return An integer indicating the success or failure of the program. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * */ int main(int argc, char **argv) { @@ -36,9 +36,9 @@ int main(int argc, char **argv) { configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. std::unique_ptr> data; - ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); + ExaGeoStat::ExaGeoStatLoadData(configurations, data); // Prediction module - ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data); + ExaGeoStat::ExaGeoStatPrediction(configurations, data); return 0; } diff --git a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp index 1c80a3b4..bb770e2e 100644 --- a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp +++ b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp @@ -12,18 +12,18 @@ * @date 2024-02-04 **/ -#include #include using namespace exageostat::api; -using namespace exageostat::dataunits; +using namespace exageostat::configurations; /** * @brief Main entry point for the Data Generation & Data Modeling program. * @details This function either generates synthetic data using the ExaGeoStat library, or reads an CSV file containing real data, models it, and predicts missing values. * @param[in] argc The number of command line arguments. * @param[in] argv An array of command line argument strings. - * @return An integer indicating the success or failure of the program. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * */ int main(int argc, char **argv) { @@ -36,11 +36,11 @@ int main(int argc, char **argv) { configurations.GetGPUsNumbers()); // Load data by either read from file or create synthetic data. std::unique_ptr> data; - ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); + ExaGeoStat::ExaGeoStatLoadData(configurations, data); // Modeling module. - ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); + ExaGeoStat::ExaGeoStatDataModeling(configurations, data); // Prediction module - ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data); + ExaGeoStat::ExaGeoStatPrediction(configurations, data); return 0; } diff --git a/examples/end-to-end/DataModeling.cpp b/examples/end-to-end/DataModeling.cpp index be37b051..9c30608c 100644 --- a/examples/end-to-end/DataModeling.cpp +++ b/examples/end-to-end/DataModeling.cpp @@ -12,13 +12,10 @@ * @date 2024-02-04 **/ -#include - -#include #include using namespace exageostat::api; -using namespace exageostat::dataunits; +using namespace exageostat::configurations; /** * @brief Main entry point for the Data Modeling program. @@ -28,14 +25,15 @@ using namespace exageostat::dataunits; * the library's efficiency in handling large spatial datasets while efficiently utilizing hardware resources.. * @param[in] argc The number of command line arguments. * @param[in] argv An array of command line argument strings. - * @return An integer indicating the success or failure of the program. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * */ int main(int argc, char **argv) { // Create a new data_modeling_configurations object with the provided command line arguments and example variables Configurations configurations; configurations.InitializeArguments(argc, argv); /** - * Since this example is currently relying on user inputs instead of reading files, The following points are important to know: + * Since this example is relying on user inputs, The following points are important to know: * The N and dts have to match with the Location X, Y and Z_values you're going to provide. * You have to provide Locations and Z values in order to use Modeling without generation. */ @@ -77,7 +75,7 @@ int main(int argc, char **argv) { data->GetLocations()->SetLocationY(*location_y, N); // Modeling module. - ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data, z_matrix); + ExaGeoStat::ExaGeoStatDataModeling(configurations, data, z_matrix); // Freeing the allocated memory. delete[] z_matrix; diff --git a/examples/end-to-end/DataPrediction.cpp b/examples/end-to-end/DataPrediction.cpp index e7df1d95..23e51879 100644 --- a/examples/end-to-end/DataPrediction.cpp +++ b/examples/end-to-end/DataPrediction.cpp @@ -12,18 +12,18 @@ * @date 2024-02-04 **/ -#include #include using namespace exageostat::api; -using namespace exageostat::dataunits; +using namespace exageostat::configurations; /** * @brief Main entry point for the Data Prediction program. * @details This function predicts missing values. * @param[in] argc The number of command line arguments. * @param[in] argv An array of command line argument strings. - * @return An integer indicating the success or failure of the program. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * */ int main(int argc, char **argv) { @@ -32,7 +32,7 @@ int main(int argc, char **argv) { // Initialize the arguments with the provided command line arguments configurations.InitializeArguments(argc, argv); /** - * Since this example is currently relying on user inputs instead of reading files, The following points are important to know: + * Since this example is currently relying on user inputs, The following points are important to know: * The N and dts have to match with the Location X, Y and Z_values you're going to provide. * You have to provide Locations and Z values in order to use Modeling without generation. */ @@ -75,7 +75,7 @@ int main(int argc, char **argv) { data->GetLocations()->SetLocationY(*location_y, N); // Prediction module - ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); + ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); delete[] location_x; delete[] location_y; diff --git a/examples/hardware/CMakeLists.txt b/examples/hardware/CMakeLists.txt index 27da3399..6acda72d 100644 --- a/examples/hardware/CMakeLists.txt +++ b/examples/hardware/CMakeLists.txt @@ -4,6 +4,7 @@ # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). # @file CMakeLists.txt +# @brief Defines an executable and links it with the ExaGeoStat library and other libraries. # @version 1.1.0 # @author Mahmoud ElKarargy # @date 2024-02-14 diff --git a/examples/hardware/ExaGeoStatHardware.cpp b/examples/hardware/ExaGeoStatHardware.cpp index f08a4177..81f1c1ef 100644 --- a/examples/hardware/ExaGeoStatHardware.cpp +++ b/examples/hardware/ExaGeoStatHardware.cpp @@ -11,12 +11,21 @@ * @date 2024-02-14 **/ -#include #include #include -using namespace exageostat::common; +using namespace exageostat::configurations; +/** + * @brief Main entry point for demonstrating the ExaGeoStatHardware class usage. + * @details This program demonstrates how to initialize the ExaGeoStatHardware class using configuration settings derived from command-line arguments. + * It showcases the initialization process for hardware configurations, including the computation mode, number of cores, and number of GPUs, + * highlighting the simplicity and effectiveness of managing hardware resources in ExaGeoStat. + * @param argc Number of command-line arguments. + * @param argv Array of command-line argument strings. + * @return An integer indicating the success or failure of the program. A return value of 0 indicates success, while any non-zero value indicates failure. + * + */ int main(int argc, char **argv) { LOGGER("** Example of Hardware **") @@ -27,4 +36,6 @@ int main(int argc, char **argv) { // Initialize Hardware auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), configuration.GetGPUsNumbers()); + + return 0; } \ No newline at end of file diff --git a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp index 41966ed7..936695c5 100644 --- a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp +++ b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp @@ -21,7 +21,6 @@ #include #include -#include #include namespace exageostat::adapters { @@ -52,17 +51,6 @@ namespace exageostat::adapters { * @return A pointer to a Configurations object containing the initialized settings. * */ - Configurations * - R_InitializeArguments(const int &aProblemSize, const std::string &aKernelName, const std::vector &aTileSize, - const std::vector &aP_QGrid, const int &aTimeSlot, const std::string &aComputation, - const std::string &aPrecision, const std::vector &aCoresGPUsNumber, const int &aBand, - const int &aMaxRank, const std::vector &aInitialTheta, - const std::vector> &aLowerUpperBounds, - const std::vector &aEstimatedTheta, const std::string &aVerbose, - const std::string &aDimension, const int &aMaxMleIterations, const int &aTolerance, - const std::vector &aPrediction, const std::vector &aPath, const bool &aSaveData - ); - /** * @brief Retrieves X coordinates of locations from ExaGeoStat data. @@ -99,41 +87,46 @@ namespace exageostat::adapters { /** * @brief Function to load ExaGeoStat data. - * @details This function loads data into an ExaGeoStatData object using the provided hardware configuration and computational settings. + * @details This function loads data into an ExaGeoStatData object using the provided configuration and computational settings. * It is designed to initialize the data structure necessary for subsequent statistical model operations within the ExaGeoStat framework. - * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. - * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data will be stored. * @return A pointer to an ExaGeoStatData object containing the loaded data. * */ - ExaGeoStatData *R_ExaGeoStatLoadData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, - ExaGeoStatData *apData); - /** - * @brief Function to model ExaGeoStat data. - * @details This function applies the model configurations specified in apConfigurations to the data stored in apData, using the hardware setup specified by apHardware. - * It prepares the ExaGeoStatData object for statistical analysis and prediction tasks, adjusting the internal data representations and parameters as needed. - * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. - * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. - * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data is stored and will be modeled. - * @param[in] aMeasurementsVector An optional Rcpp::Nullable object containing a vector of measurements. If provided, it is used to enhance the data modeling process. - * @param[in] aLocationsX An optional Rcpp::Nullable object containing a vector of X coordinates for the locations. If provided, it is used to enhance the data modeling process. - * @param[in] aLocationsY An optional Rcpp::Nullable object containing a vector of Y coordinates for the locations. If provided, it is used to enhance the data modeling process. - * @param[in] aLocationsZ An optional Rcpp::Nullable object containing a vector of Z coordinates for the locations. If provided, it is used to enhance the data modeling process. - * @return A pointer to an ExaGeoStatData object containing the modeled data, ready for statistical analysis and prediction. - */ - ExaGeoStatData *R_ExaGeoStatModelData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, - ExaGeoStatData *apData, - Rcpp::Nullable aMeasurementsVector = R_NilValue, - Rcpp::Nullable aLocationsX = R_NilValue, - Rcpp::Nullable aLocationsY = R_NilValue, - Rcpp::Nullable aLocationsZ = R_NilValue); + ExaGeoStatData * + R_ExaGeoStatLoadData(const std::string &aKernelName, const std::vector &aInitialTheta, + const std::string &aDistanceMatrix, const int &aProblemSize, const int &aSeed, + const int &aDenseTileSize, const int &aLowTileSize, const std::string &aDimension, + const std::string &aLogPath, const std::string &aDataPath, + const std::string &aRecoveryFilePath, const std::string &aObservationsFilePath); + + /** + * @brief Function to model ExaGeoStat data. + * @details This function applies the model configurations specified in apConfigurations to the data stored in apData. + * It prepares the ExaGeoStatData object for statistical analysis and prediction tasks, adjusting the internal data representations and parameters as needed. + * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data is stored and will be modeled. + * @param[in] aMeasurementsVector An optional Rcpp::Nullable object containing a vector of measurements. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsX An optional Rcpp::Nullable object containing a vector of X coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsY An optional Rcpp::Nullable object containing a vector of Y coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @param[in] aLocationsZ An optional Rcpp::Nullable object containing a vector of Z coordinates for the locations. If provided, it is used to enhance the data modeling process. + * @return A pointer to an ExaGeoStatData object containing the modeled data, ready for statistical analysis and prediction. + */ + std::vector R_ExaGeoStatModelData(const std::string &aComputation, const std::string &aKernelName, + const std::string &aDistanceMatrix, + const std::vector &aLowerBound, + const std::vector &aUpperBound, const int &aTolerance, + const int &aMleIterations, const int &aDenseTileSize, + const int &aLowTileSize, const std::string &aDimension, const int &aBand, const int &aMaxRank, + SEXP apData, + Rcpp::Nullable aMeasurementsVector = R_NilValue, + Rcpp::Nullable aLocationsX = R_NilValue, + Rcpp::Nullable aLocationsY = R_NilValue, + Rcpp::Nullable aLocationsZ = R_NilValue); /** * @brief Function to predict using ExaGeoStat data. * @details This function performs predictions based on the modeled ExaGeoStatData object. - * It utilizes the hardware configuration and computational settings defined by apHardware and apConfigurations, respectively, to execute prediction algorithms on the data stored in apData. - * @param[in] apHardware A pointer to an ExaGeoStatHardware object representing the hardware configuration. + * It utilizes the configuration and computational settings defined by apConfigurations, respectively, to execute prediction algorithms on the data stored in apData. * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data is stored and will be modeled. * @param[in] aMeasurementsVector An optional Rcpp::Nullable object containing a vector of measurements. If provided, it is used to enhance the data modeling process. @@ -142,12 +135,36 @@ namespace exageostat::adapters { * @param[in] aLocationsZ An optional Rcpp::Nullable object containing a vector of Z coordinates for the locations. If provided, it is used to enhance the data modeling process. * @return A pointer to an ExaGeoStatData object containing the modeled data, ready for statistical analysis and prediction. */ - ExaGeoStatData *R_ExaGeoStatPredictData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, - ExaGeoStatData *apData, - Rcpp::Nullable aMeasurementsVector = R_NilValue, - Rcpp::Nullable aLocationsX = R_NilValue, - Rcpp::Nullable aLocationsY = R_NilValue, - Rcpp::Nullable aLocationsZ = R_NilValue); + std::vector R_ExaGeoStatPredictData(const std::string &aKernelName, const std::string &aDistanceMatrix, + const std::vector &aEstimatedTheta, + const int &aDenseTileSize, const int &aLowTileSize, + const std::string &aDimension, std::vector> &aTrainData, + std::vector> &aTestData); + + std::vector R_ExaGeoStatMLOE_MMOM(const std::string &aKernelName, const std::string &aDistanceMatrix, + const std::vector &aEstimatedTheta, + const std::vector &aTrueTheta, + const int &aDenseTileSize, const int &aLowTileSize, + const std::string &aDimension, std::vector> &aTrainData, + std::vector> &aTestData); + + std::vector R_ExaGeoStatFisher(const std::string &aKernelName, const std::string &aDistanceMatrix, + const std::vector &aEstimatedTheta, + const int &aDenseTileSize, + const int &aLowTileSize, const std::string &aDimension, + std::vector> &aTrainData, + std::vector> &aTestData); + + std::vector R_ExaGeoStatIDW(const std::string &aKernelName, const std::string &aDistanceMatrix, + const std::vector &aEstimatedTheta, + const int &aDenseTileSize, const int &aLowTileSize, + const std::string &aDimension, + std::vector> &aTrainData, + std::vector> &aTestData, std::vector &aTestMeasurementsValues); + + double *GetDataFromArguments(Rcpp::Nullable aMeasurementsVector, Rcpp::Nullable aLocationsX,Rcpp::Nullable aLocationsY, Rcpp::Nullable aLocationsZ, std::unique_ptr> &aData, configurations::Configurations &aConfigurations, + const std::string &aKernelName, const std::string &aDistanceMatrix, + const int &aDenseTileSize, const int &aLowTileSize, const std::string &aDimension, const common::Computation &aComputation); } #endif //EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP diff --git a/inst/include/api/ExaGeoStat.hpp b/inst/include/api/ExaGeoStat.hpp index b2f93ad3..86a15c57 100644 --- a/inst/include/api/ExaGeoStat.hpp +++ b/inst/include/api/ExaGeoStat.hpp @@ -19,7 +19,6 @@ #include #include #include -#include namespace exageostat::api { /** @@ -33,27 +32,23 @@ namespace exageostat::api { /** * @brief Generates Data whether it's synthetic data or real. - * @param[in] aHardware Reference to Hardware configuration for the ExaGeoStat solver. * @param[in] aConfigurations Reference to Configurations object containing user input data. * @param[out] aData Reference to an ExaGeoStatData object where generated data will be stored. * @return void * */ - static void ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, - Configurations &aConfigurations, + static void ExaGeoStatLoadData(configurations::Configurations &aConfigurations, std::unique_ptr> &aData); /** * @brief Models Data whether it's synthetic data or real. - * @param[in] aHardware Reference to Hardware configuration for the ExaGeoStat solver. * @param[in] aConfigurations Reference to Configurations object containing user input data. * @param[in] aData Reference to an ExaGeoStatData object containing needed descriptors, and locations. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. * @return the last optimum value of MLE. * */ - static T ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, - Configurations &aConfigurations, + static T ExaGeoStatDataModeling(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, T *apMeasurementsMatrix = nullptr); @@ -69,16 +64,15 @@ namespace exageostat::api { /** * @brief Predict missing measurements values. - * @param[in] aHardware Reference to Hardware configuration for the ExaGeoStat solver. * @param[in] aConfigurations Reference to Configurations object containing user input data. * @param[in, out] aData Reference to an ExaGeoStatData object containing needed descriptors, and locations. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. * @return void */ - static void ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, - Configurations &aConfigurations, - std::unique_ptr> &aData, - T *apMeasurementsMatrix = nullptr); + static void ExaGeoStatPrediction(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, + T *apMeasurementsMatrix = nullptr, + dataunits::Locations *apTrainLocations = nullptr, + dataunits::Locations *apTestLocations = nullptr); private: /** diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index e3c55831..cc50964c 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -59,316 +59,311 @@ type Get##name() return std::any_cast(mDictionary[dictionary_name]); \ } -/** - * @class Configurations - * @brief Contains methods to set and get. - * - */ -class Configurations { -public: - +namespace exageostat::configurations { /** - * @brief Constructor initializing a Configuration object with default values. + * @class Configurations + * @brief Contains methods to set and get. * */ - Configurations(); + class Configurations { + public: - /** - * @brief destructor to allow calls to the correct concrete destructor. - * - */ - ~Configurations(); + /** + * @brief Constructor initializing a Configuration object with default values. + * + */ + Configurations(); - /** - * @brief Initialize the module arguments. - * @param[in] aArgC The number of arguments being passed into the program from the command line. - * @param[in] apArgV The array of arguments. - * @param[in] aEnableR check if R is enabled - * @details This method initializes the command line arguments and set default values for unused args. - * - */ - void InitializeArguments(const int &aArgC, char **apArgV, const bool &aEnableR = false); + /** + * @brief destructor to allow calls to the correct concrete destructor. + * + */ + ~Configurations(); - /** - * @brief Initialize the module arguments. - * @param[in] aArgC The number of arguments being passed into the program from the command line. - * @param[in] apArgV The array of arguments. - * @details This method initializes the command line arguments and set default values for unused args. - * - */ + /** + * @brief Initialize the module arguments. + * @param[in] aArgC The number of arguments being passed into the program from the command line. + * @param[in] apArgV The array of arguments. + * @param[in] aEnableR check if R is enabled + * @details This method initializes the command line arguments and set default values for unused args. + * + */ + void InitializeArguments(const int &aArgC, char **apArgV, const bool &aEnableR = false); - /** - * @brief Initialize the all theta arguments. - * @return void - */ - void InitializeAllTheta(); + /** + * @brief Initialize the all theta arguments. + * @return void + */ + void InitializeAllTheta(); - /** - * @brief Initialize data generation arguments.. - * @return void - * - */ - void InitializeDataGenerationArguments(); + /** + * @brief Initialize data generation arguments.. + * @return void + * + */ + void InitializeDataGenerationArguments(); - /** - * @brief Initialize data Modeling arguments. - * @return void - * - */ - void InitializeDataModelingArguments(); + /** + * @brief Initialize data Modeling arguments. + * @return void + * + */ + void InitializeDataModelingArguments(); - /** - * @brief Initialize data Prediction arguments. - * @return void - * - */ - void InitializeDataPredictionArguments(); + /** + * @brief Initialize data Prediction arguments. + * @return void + * + */ + void InitializeDataPredictionArguments(); - /** - * @brief Print the usage and accepted Arguments. - * @return void - * - */ - static void PrintUsage(); + /** + * @brief Print the usage and accepted Arguments. + * @return void + * + */ + static void PrintUsage(); - /** START OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ + /** START OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ - CREATE_SETTER_FUNCTION(ProblemSize, int, aProblemSize, "ProblemSize") + CREATE_SETTER_FUNCTION(ProblemSize, int, aProblemSize, "ProblemSize") - CREATE_GETTER_FUNCTION(ProblemSize, int, "ProblemSize") + CREATE_GETTER_FUNCTION(ProblemSize, int, "ProblemSize") - CREATE_SETTER_FUNCTION(KernelName, const std::string&, aKernel, "Kernel") + CREATE_SETTER_FUNCTION(KernelName, const std::string&, aKernel, "Kernel") - CREATE_GETTER_FUNCTION(KernelName, const std::string&, "Kernel") + CREATE_GETTER_FUNCTION(KernelName, const std::string&, "Kernel") - CREATE_SETTER_FUNCTION(PGrid, int, aPGrid, "PGrid") + CREATE_SETTER_FUNCTION(PGrid, int, aPGrid, "PGrid") - CREATE_GETTER_FUNCTION(PGrid, int, "PGrid") + CREATE_GETTER_FUNCTION(PGrid, int, "PGrid") - CREATE_SETTER_FUNCTION(QGrid, int, aQGrid, "QGrid") + CREATE_SETTER_FUNCTION(QGrid, int, aQGrid, "QGrid") - CREATE_GETTER_FUNCTION(QGrid, int, "QGrid") + CREATE_GETTER_FUNCTION(QGrid, int, "QGrid") - CREATE_SETTER_FUNCTION(TimeSlot, int, aTimeSlot, "TimeSlot") + CREATE_SETTER_FUNCTION(TimeSlot, int, aTimeSlot, "TimeSlot") - CREATE_GETTER_FUNCTION(TimeSlot, int, "TimeSlot") + CREATE_GETTER_FUNCTION(TimeSlot, int, "TimeSlot") - CREATE_SETTER_FUNCTION(Computation, exageostat::common::Computation, aComputation, "Computation") + CREATE_SETTER_FUNCTION(Computation, common::Computation, aComputation, "Computation") - CREATE_GETTER_FUNCTION(Computation, exageostat::common::Computation, "Computation") + CREATE_GETTER_FUNCTION(Computation, common::Computation, "Computation") - CREATE_SETTER_FUNCTION(Precision, exageostat::common::Precision, aPrecision, "Precision") + CREATE_SETTER_FUNCTION(Precision, common::Precision, aPrecision, "Precision") - CREATE_GETTER_FUNCTION(Precision, exageostat::common::Precision, "Precision") + CREATE_GETTER_FUNCTION(Precision, common::Precision, "Precision") - CREATE_SETTER_FUNCTION(CoresNumber, int, aCoresNumbers, "CoresNumbers") + CREATE_SETTER_FUNCTION(CoresNumber, int, aCoresNumbers, "CoresNumbers") - CREATE_GETTER_FUNCTION(CoresNumber, int, "CoresNumbers") + CREATE_GETTER_FUNCTION(CoresNumber, int, "CoresNumbers") - CREATE_SETTER_FUNCTION(GPUsNumbers, int, aGPUsNumber, "GPUsNumbers") + CREATE_SETTER_FUNCTION(GPUsNumbers, int, aGPUsNumber, "GPUsNumbers") - CREATE_GETTER_FUNCTION(GPUsNumbers, int, "GPUsNumbers") + CREATE_GETTER_FUNCTION(GPUsNumbers, int, "GPUsNumbers") - CREATE_SETTER_FUNCTION(DenseTileSize, int, aTileSize, "DTS") + CREATE_SETTER_FUNCTION(DenseTileSize, int, aTileSize, "DTS") - CREATE_GETTER_FUNCTION(DenseTileSize, int, "DTS") + CREATE_GETTER_FUNCTION(DenseTileSize, int, "DTS") - CREATE_SETTER_FUNCTION(LowTileSize, int, aTileSize, "LTS") + CREATE_SETTER_FUNCTION(LowTileSize, int, aTileSize, "LTS") - CREATE_GETTER_FUNCTION(LowTileSize, int, "LTS") + CREATE_GETTER_FUNCTION(LowTileSize, int, "LTS") - CREATE_SETTER_FUNCTION(Band, int, aBand, "Band") + CREATE_SETTER_FUNCTION(Band, int, aBand, "Band") - CREATE_GETTER_FUNCTION(Band, int, "Band") + CREATE_GETTER_FUNCTION(Band, int, "Band") - CREATE_SETTER_FUNCTION(MaxRank, int, aMaxRank, "MaxRank") + CREATE_SETTER_FUNCTION(MaxRank, int, aMaxRank, "MaxRank") - CREATE_GETTER_FUNCTION(MaxRank, int, "MaxRank") + CREATE_GETTER_FUNCTION(MaxRank, int, "MaxRank") - CREATE_SETTER_FUNCTION(ActualObservationsFilePath, const std::string &, aActualObservationsFilePath, - "ActualObservationsFilePath") + CREATE_SETTER_FUNCTION(ActualObservationsFilePath, const std::string &, aActualObservationsFilePath, + "ActualObservationsFilePath") - CREATE_GETTER_FUNCTION(ActualObservationsFilePath, std::string, "ActualObservationsFilePath") + CREATE_GETTER_FUNCTION(ActualObservationsFilePath, std::string, "ActualObservationsFilePath") - CREATE_SETTER_FUNCTION(Seed, int, aSeed, "Seed") + CREATE_SETTER_FUNCTION(Seed, int, aSeed, "Seed") - CREATE_GETTER_FUNCTION(Seed, int, "Seed") + CREATE_GETTER_FUNCTION(Seed, int, "Seed") - CREATE_SETTER_FUNCTION(LoggerPath, const std::string&, aLoggerPath, "LoggerPath") + CREATE_SETTER_FUNCTION(LoggerPath, const std::string&, aLoggerPath, "LoggerPath") - CREATE_GETTER_FUNCTION(LoggerPath, std::string, "LoggerPath") + CREATE_GETTER_FUNCTION(LoggerPath, std::string, "LoggerPath") - CREATE_SETTER_FUNCTION(InitialTheta, std::vector &, apTheta, "InitialTheta") + CREATE_SETTER_FUNCTION(InitialTheta, const std::vector &, apTheta, "InitialTheta") - CREATE_GETTER_FUNCTION(InitialTheta, std::vector &, "InitialTheta") + CREATE_GETTER_FUNCTION(InitialTheta, std::vector &, "InitialTheta") - CREATE_SETTER_FUNCTION(IsOOC, bool, aIsOOC, "OOC") + CREATE_SETTER_FUNCTION(IsOOC, bool, aIsOOC, "OOC") - CREATE_GETTER_FUNCTION(IsOOC, bool, "OOC") + CREATE_GETTER_FUNCTION(IsOOC, bool, "OOC") - CREATE_SETTER_FUNCTION(ApproximationMode, int, aApproximationMode, "ApproximationMode") + CREATE_SETTER_FUNCTION(ApproximationMode, int, aApproximationMode, "ApproximationMode") - CREATE_GETTER_FUNCTION(ApproximationMode, int, "ApproximationMode") + CREATE_GETTER_FUNCTION(ApproximationMode, int, "ApproximationMode") - CREATE_SETTER_FUNCTION(Logger, bool, aLogger, "Logger") + CREATE_SETTER_FUNCTION(Logger, bool, aLogger, "Logger") - CREATE_GETTER_FUNCTION(Logger, bool, "Logger") + CREATE_GETTER_FUNCTION(Logger, bool, "Logger") - CREATE_SETTER_FUNCTION(LowerBounds, std::vector &, apTheta, "LowerBounds") + CREATE_SETTER_FUNCTION(LowerBounds, const std::vector &, apTheta, "LowerBounds") - CREATE_GETTER_FUNCTION(LowerBounds, std::vector &, "LowerBounds") + CREATE_GETTER_FUNCTION(LowerBounds, std::vector &, "LowerBounds") - CREATE_SETTER_FUNCTION(UpperBounds, std::vector &, apTheta, "UpperBounds") + CREATE_SETTER_FUNCTION(UpperBounds, const std::vector &, apTheta, "UpperBounds") - CREATE_GETTER_FUNCTION(UpperBounds, std::vector &, "UpperBounds") + CREATE_GETTER_FUNCTION(UpperBounds, std::vector &, "UpperBounds") - CREATE_SETTER_FUNCTION(EstimatedTheta, std::vector &, apTheta, "EstimatedTheta") + CREATE_SETTER_FUNCTION(EstimatedTheta, const std::vector &, apTheta, "EstimatedTheta") - CREATE_GETTER_FUNCTION(EstimatedTheta, std::vector &, "EstimatedTheta") + CREATE_GETTER_FUNCTION(EstimatedTheta, std::vector &, "EstimatedTheta") - CREATE_SETTER_FUNCTION(StartingTheta, std::vector &, apTheta, "StartingTheta") + CREATE_SETTER_FUNCTION(StartingTheta, const std::vector &, apTheta, "StartingTheta") - CREATE_GETTER_FUNCTION(StartingTheta, std::vector &, "StartingTheta") + CREATE_GETTER_FUNCTION(StartingTheta, std::vector &, "StartingTheta") - CREATE_SETTER_FUNCTION(IsNonGaussian, bool, aIsNonGaussian, "IsNonGaussian") + CREATE_SETTER_FUNCTION(IsNonGaussian, bool, aIsNonGaussian, "IsNonGaussian") - CREATE_GETTER_FUNCTION(IsNonGaussian, bool, "IsNonGaussian") + CREATE_GETTER_FUNCTION(IsNonGaussian, bool, "IsNonGaussian") - /** - * @brief Getter for the verbosity. - * @return The verbosity mode. - * - */ - static exageostat::common::Verbose GetVerbosity(); + /** + * @brief Getter for the verbosity. + * @return The verbosity mode. + * + */ + static exageostat::common::Verbose GetVerbosity(); - static void SetVerbosity(const exageostat::common::Verbose &aVerbose); + static void SetVerbosity(const common::Verbose &aVerbose); - /** END OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ - /** START OF THE DATA GENERATION MODULES. **/ + /** END OF THE COMMON ARGUMENTS BETWEEN ALL MODULES. **/ + /** START OF THE DATA GENERATION MODULES. **/ - CREATE_SETTER_FUNCTION(Dimension, exageostat::common::Dimension, aDimension, "Dimension") + CREATE_SETTER_FUNCTION(Dimension, exageostat::common::Dimension, aDimension, "Dimension") - CREATE_GETTER_FUNCTION(Dimension, exageostat::common::Dimension, "Dimension") + CREATE_GETTER_FUNCTION(Dimension, exageostat::common::Dimension, "Dimension") - CREATE_SETTER_FUNCTION(IsSynthetic, bool, aIsSynthetic, "IsSynthetic") + CREATE_SETTER_FUNCTION(IsSynthetic, bool, aIsSynthetic, "IsSynthetic") - CREATE_GETTER_FUNCTION(IsSynthetic, bool, "IsSynthetic") + CREATE_GETTER_FUNCTION(IsSynthetic, bool, "IsSynthetic") - CREATE_SETTER_FUNCTION(DataPath, const std::string&, aDataPath, "DataPath") + CREATE_SETTER_FUNCTION(DataPath, const std::string&, aDataPath, "DataPath") - CREATE_GETTER_FUNCTION(DataPath, std::string, "DataPath") + CREATE_GETTER_FUNCTION(DataPath, std::string, "DataPath") - /** END OF THE DATA GENERATION MODULES. **/ - /** START OF THE DATA MODELING MODULES. **/ + /** END OF THE DATA GENERATION MODULES. **/ + /** START OF THE DATA MODELING MODULES. **/ - CREATE_SETTER_FUNCTION(RecoveryFile, const std::string&, aRecoveryFile, "RecoveryFile") + CREATE_SETTER_FUNCTION(RecoveryFile, const std::string&, aRecoveryFile, "RecoveryFile") - CREATE_GETTER_FUNCTION(RecoveryFile, std::string, "RecoveryFile") + CREATE_GETTER_FUNCTION(RecoveryFile, std::string, "RecoveryFile") - CREATE_SETTER_FUNCTION(FileLogPath, FILE *, apFileLogPath, "FileLogPath") + CREATE_SETTER_FUNCTION(FileLogPath, FILE *, apFileLogPath, "FileLogPath") - CREATE_GETTER_FUNCTION(FileLogPath, FILE *, "FileLogPath") + CREATE_GETTER_FUNCTION(FileLogPath, FILE *, "FileLogPath") - CREATE_SETTER_FUNCTION(FileLogName, const std::string&, aFileLogName, "FileLogName") + CREATE_SETTER_FUNCTION(FileLogName, const std::string&, aFileLogName, "FileLogName") - CREATE_GETTER_FUNCTION(FileLogName, std::string, "FileLogName") + CREATE_SETTER_FUNCTION(DistanceMetric, common::DistanceMetric, aDistanceMetric, "DistanceMetric") - CREATE_SETTER_FUNCTION(DistanceMetric, exageostat::common::DistanceMetric, aDistanceMetric, "DistanceMetric") + CREATE_GETTER_FUNCTION(DistanceMetric, common::DistanceMetric, "DistanceMetric") - CREATE_GETTER_FUNCTION(DistanceMetric, exageostat::common::DistanceMetric, "DistanceMetric") + CREATE_SETTER_FUNCTION(MaxMleIterations, int, aMaxMleIterations, "MaxMleIterations") - CREATE_SETTER_FUNCTION(MaxMleIterations, int, aMaxMleIterations, "MaxMleIterations") + CREATE_GETTER_FUNCTION(MaxMleIterations, int, "MaxMleIterations") - CREATE_GETTER_FUNCTION(MaxMleIterations, int, "MaxMleIterations") + CREATE_SETTER_FUNCTION(Accuracy, int, aAccuracy, "Accuracy") - CREATE_SETTER_FUNCTION(Accuracy, int, aAccuracy, "Accuracy") + CREATE_GETTER_FUNCTION(Accuracy, int, "Accuracy") - CREATE_GETTER_FUNCTION(Accuracy, int, "Accuracy") + void SetTolerance(double aTolerance); - CREATE_SETTER_FUNCTION(Tolerance, double, aTolerance, "Tolerance") + CREATE_GETTER_FUNCTION(Tolerance, double, "Tolerance") - CREATE_GETTER_FUNCTION(Tolerance, double, "Tolerance") + /** END OF THE DATA MODELING MODULES. **/ + /** START OF THE DATA PREDICTION MODULES. **/ - /** END OF THE DATA MODELING MODULES. **/ - /** START OF THE DATA PREDICTION MODULES. **/ + CREATE_SETTER_FUNCTION(UnknownObservationsNb, int, aUnknownObservationsNumber, "UnknownObservationsNb") - CREATE_SETTER_FUNCTION(UnknownObservationsNb, int, aUnknownObservationsNumber, "UnknownObservationsNb") + CREATE_GETTER_FUNCTION(UnknownObservationsNb, int, "UnknownObservationsNb") - CREATE_GETTER_FUNCTION(UnknownObservationsNb, int, "UnknownObservationsNb") + CREATE_SETTER_FUNCTION(IsMSPE, bool, aIsMSPE, "IsMSPE") - CREATE_SETTER_FUNCTION(IsMSPE, bool, aIsMSPE, "IsMSPE") + CREATE_GETTER_FUNCTION(IsMSPE, bool, "IsMSPE") - CREATE_GETTER_FUNCTION(IsMSPE, bool, "IsMSPE") + CREATE_SETTER_FUNCTION(IsIDW, bool, aIsIDW, "IsIDW") - CREATE_SETTER_FUNCTION(IsIDW, bool, aIsIDW, "IsIDW") + CREATE_GETTER_FUNCTION(IsIDW, bool, "IsIDW") - CREATE_GETTER_FUNCTION(IsIDW, bool, "IsIDW") + CREATE_SETTER_FUNCTION(IsMLOEMMOM, bool, aIsMLOEMMOM, "IsMLOEMMOM") - CREATE_SETTER_FUNCTION(IsMLOEMMOM, bool, aIsMLOEMMOM, "IsMLOEMMOM") + CREATE_GETTER_FUNCTION(IsMLOEMMOM, bool, "IsMLOEMMOM") - CREATE_GETTER_FUNCTION(IsMLOEMMOM, bool, "IsMLOEMMOM") + CREATE_SETTER_FUNCTION(IsFisher, bool, aIsFisher, "IsFisher") - CREATE_SETTER_FUNCTION(IsFisher, bool, aIsFisher, "IsFisher") + CREATE_GETTER_FUNCTION(IsFisher, bool, "IsFisher") - CREATE_GETTER_FUNCTION(IsFisher, bool, "IsFisher") + CREATE_SETTER_FUNCTION(ObservationNumber, int, aObservationsNumber, "ObservationNumber") - /** END OF THE DATA PREDICTION MODULES. **/ + CREATE_GETTER_FUNCTION(ObservationNumber, int, "ObservationNumber") - /** - * @brief Check if input value is numerical. - * @param[in] aValue The input from the user side. - * @return The int casted value. - * - */ - static int CheckNumericalValue(const std::string &aValue); + /** END OF THE DATA PREDICTION MODULES. **/ - /** - * @brief Checks the value of the dimension parameter. - * @param[in] aDimension A string representing the dimension. - * @return The corresponding dimension value. - * - */ - static exageostat::common::Dimension CheckDimensionValue(const std::string &aDimension); + /** + * @brief Check if input value is numerical. + * @param[in] aValue The input from the user side. + * @return The int casted value. + * + */ + static int CheckNumericalValue(const std::string &aValue); - /** - * @brief Checks if the kernel value is valid. - * @param[in] aKernel The kernel to check. - * @return void - * - */ - void CheckKernelValue(const std::string &aKernel); + /** + * @brief Checks the value of the dimension parameter. + * @param[in] aDimension A string representing the dimension. + * @return The corresponding dimension value. + * + */ + static exageostat::common::Dimension CheckDimensionValue(const std::string &aDimension); - /** - * @brief Check input computation value. - * @param[in] aValue The input from the user side. - * @return Enum with the selected computation, Error if not exist. - * - */ - static exageostat::common::Computation CheckComputationValue(const std::string &aValue); + /** + * @brief Checks if the kernel value is valid. + * @param[in] aKernel The kernel to check. + * @return void + * + */ + void CheckKernelValue(const std::string &aKernel); - /** - * @brief Check input precision value. - * @param[in] aValue The input from the user side. - * @return Enum with the selected Precision, Error if not exist. - * - */ - static exageostat::common::Precision CheckPrecisionValue(const std::string &aValue); + /** + * @brief Check input computation value. + * @param[in] aValue The input from the user side. + * @return Enum with the selected computation, Error if not exist. + * + */ + static common::Computation CheckComputationValue(const std::string &aValue); - /** - * @brief Checks the value of the unknown observations parameter. - * @param[in] aValue A string representing the number of unknown observations. - * @return The corresponding integer value. - */ - int CheckUnknownObservationsValue(const std::string &aValue); + /** + * @brief Check input precision value. + * @param[in] aValue The input from the user side. + * @return Enum with the selected Precision, Error if not exist. + * + */ + static common::Precision CheckPrecisionValue(const std::string &aValue); - /** - * @brief Initialize a vector with a given size to contain zeros. - * @param[in, out] aTheta A reference to the vector to initialize. - * @param[in] aSize The size of the vector to initialize. - * @return void. - * - */ - static void InitTheta(std::vector &aTheta, const int &aSize); + /** + * @brief Checks the value of the unknown observations parameter. + * @param[in] aValue A string representing the number of unknown observations. + * @return The corresponding integer value. + */ + int CheckUnknownObservationsValue(const std::string &aValue); + + /** + * @brief Initialize a vector with a given size to contain zeros. + * @param[in, out] aTheta A reference to the vector to initialize. + * @param[in] aSize The size of the vector to initialize. + * @return void. + * + */ + static void InitTheta(std::vector &aTheta, const int &aSize); /** * @brief print the summary of MLE inputs. @@ -377,60 +372,61 @@ class Configurations { */ inline void PrintSummary(int aRank = 0); - /** - * @brief Calculates the number of observed measurements. - * @return number of observed measurements. - */ - int CalculateZObsNumber(); + /** + * @brief Calculates the number of observed measurements. + * @return number of observed measurements. + */ + int CalculateZObsNumber(); + /** + * @brief Parses a string of theta values and returns an array of doubles. + * @param[in] aInputValues The input string of theta values. + * @return A vector of parsed theta values. + * + */ + static std::vector ParseTheta(const std::string &aInputValues); -private: - /** - * @brief Checks the run mode and sets the verbosity level. - * @param[in] aVerbosity A string representing the desired run mode ("verbose" or "standard"). - * @throws std::range_error if the input string is not "verbose" or "standard". - * @return void - * - */ - static void ParseVerbose(const std::string &aVerbosity); + /** + * @brief parse user's input to distance metric. + * @param[in] aDistanceMetric string specifying the used distance metric. + * @return void + */ + void ParseDistanceMetric(const std::string &aDistanceMetric); - /** - * @brief Checks if a given string is in camel case format. - * @param[in] aString The string to check. - * @return true if the string is in camel case format, false otherwise. - * - */ - static bool IsCamelCase(const std::string &aString); + private: - /** - * @brief Parses a string of theta values and returns an array of doubles. - * @param[in] aInputValues The input string of theta values. - * @return A vector of parsed theta values. - * - */ - static std::vector ParseTheta(const std::string &aInputValues); + /** + * @brief Checks the run mode and sets the verbosity level. + * @param[in] aVerbosity A string representing the desired run mode ("verbose" or "standard"). + * @throws std::range_error if the input string is not "verbose" or "standard". + * @return void + * + */ + static void ParseVerbose(const std::string &aVerbosity); - /** - * @brief parse user's input to distance metric. - * @param[in] aDistanceMetric string specifying the used distance metric. - * @return void - */ - void ParseDistanceMetric(const std::string &aDistanceMetric); - - /// Used Dictionary - std::unordered_map mDictionary; - /// Used Argument counter - int mArgC = 0; - /// Used Argument vectors - char **mpArgV = nullptr; - //// Used run mode - static exageostat::common::Verbose mVerbosity; - //// Used bool for init theta - static bool mIsThetaInit; - //// Used bool for R allocated memory on heap - static bool mHeapAllocated; -}; + /** + * @brief Checks if a given string is in camel case format. + * @param[in] aString The string to check. + * @return true if the string is in camel case format, false otherwise. + * + */ + static bool IsCamelCase(const std::string &aString); + + /// Used Dictionary + std::unordered_map mDictionary; + /// Used Argument counter + int mArgC = 0; + /// Used Argument vectors + char **mpArgV = nullptr; + //// Used run mode + static exageostat::common::Verbose mVerbosity; + //// Used bool for init theta + static bool mIsThetaInit; + //// Used bool for R allocated memory on heap + static bool mHeapAllocated; + }; +}//namespace exageostat #endif //EXAGEOSTAT_CPP_CONFIGURATIONS_HPP diff --git a/inst/include/data-generators/DataGenerator.hpp b/inst/include/data-generators/DataGenerator.hpp index d040caa7..05b4fe9d 100644 --- a/inst/include/data-generators/DataGenerator.hpp +++ b/inst/include/data-generators/DataGenerator.hpp @@ -34,14 +34,12 @@ namespace exageostat::generators { * @brief Either generates synthetic data or reads data files. * @details This method generates the X, Y, and Z variables used to define the locations of the data points. * @param[in] aConfigurations Reference to the data configurations. - * @param[in] aHardware Reference to the used hardware. * @param[in] aKernel Reference to the used Kernel. * @return unique Pointer to a populated data. * */ virtual std::unique_ptr> - CreateData(Configurations &aConfigurations, - const ExaGeoStatHardware &aHardware, + CreateData(configurations::Configurations &aConfigurations, exageostat::kernels::Kernel &aKernel) = 0; /** @@ -52,7 +50,7 @@ namespace exageostat::generators { * */ static std::unique_ptr - CreateGenerator(Configurations &aConfigurations); + CreateGenerator(configurations::Configurations &aConfigurations); /** * @brief Destructor for the data generator object. diff --git a/inst/include/data-generators/concrete/SyntheticGenerator.hpp b/inst/include/data-generators/concrete/SyntheticGenerator.hpp index 62f86b4c..b61c366d 100644 --- a/inst/include/data-generators/concrete/SyntheticGenerator.hpp +++ b/inst/include/data-generators/concrete/SyntheticGenerator.hpp @@ -44,8 +44,7 @@ namespace exageostat::generators::synthetic { * */ std::unique_ptr> - CreateData(Configurations &aConfigurations, - const ExaGeoStatHardware &aHardware, + CreateData(configurations::Configurations &aConfigurations, exageostat::kernels::Kernel &aKernel) override; /** diff --git a/inst/include/data-loader/DataLoader.hpp b/inst/include/data-loader/DataLoader.hpp index b14e5371..62c4c937 100644 --- a/inst/include/data-loader/DataLoader.hpp +++ b/inst/include/data-loader/DataLoader.hpp @@ -36,8 +36,7 @@ namespace exageostat::dataLoader { * */ std::unique_ptr> - CreateData(Configurations &aConfigurations, const ExaGeoStatHardware &aHardware, - kernels::Kernel &aKernel) override; + CreateData(configurations::Configurations &aConfigurations, kernels::Kernel &aKernel) override; /** * @brief Reads data from external sources into ExaGeoStat format. @@ -49,9 +48,22 @@ namespace exageostat::dataLoader { * @param aP Partition index for distributed data loading. */ virtual void - ReadData(Configurations &aConfigurations, std::vector &aMeasurementsMatrix, std::vector &aXLocations, + ReadData(configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, const int &aP) = 0; + /** + * @brief Writes a matrix of vectors to disk. + * @param[in] aMatrixPointer A Reference to the matrix data. + * @param[in] aProblemSize The size of the problem. + * @param[in] aP The number of processes. + * @param[in] aLoggerPath The path to the logger file. + * @param[in] aLocations A Reference to the Locations object. + * @return void + * + */ + virtual void + WriteData(const T &aMatrixPointer, const int &aProblemSize, const int &aP, std::string &aLoggerPath, + exageostat::dataunits::Locations &aLocations) = 0; }; /** @@ -60,7 +72,6 @@ namespace exageostat::dataLoader { * */ EXAGEOSTAT_INSTANTIATE_CLASS(DataLoader) - } // namespace exageostat #endif //EXAGEOSTATCPP_DATALOADER_HPP diff --git a/inst/include/data-loader/concrete/CSVLoader.hpp b/inst/include/data-loader/concrete/CSVLoader.hpp index 56d84334..d6bb924a 100644 --- a/inst/include/data-loader/concrete/CSVLoader.hpp +++ b/inst/include/data-loader/concrete/CSVLoader.hpp @@ -41,10 +41,19 @@ namespace exageostat::dataLoader::csv { * @copydoc DataLoader::ReadData() * */ - void ReadData(Configurations &aConfigurations, std::vector &aMeasurementsMatrix, + void ReadData(configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, const int &aP) override; + /** + * @brief Writes a matrix of vectors to disk. + * @copydoc DataLoader::WriteData() + * + */ + void + WriteData(const T &aMatrixPointer, const int &aProblemSize, const int &aP, std::string &aLoggerPath, + exageostat::dataunits::Locations &aLocations) override; + /** * @brief Release the singleton instance of the CSVLoader class. * @return void diff --git a/inst/include/data-units/ModelingDataHolders.hpp b/inst/include/data-units/ModelingDataHolders.hpp index 0d858dc6..3eadd519 100644 --- a/inst/include/data-units/ModelingDataHolders.hpp +++ b/inst/include/data-units/ModelingDataHolders.hpp @@ -20,9 +20,7 @@ namespace exageostat::dataunits { /// ExaGeoStatData object containing needed descriptors, and locations. std::unique_ptr> *mpData; /// Configurations object containing user input data. - Configurations *mpConfiguration; - /// Hardware configuration for the ExaGeoStat solver. - const ExaGeoStatHardware *mpHardware; + configurations::Configurations *mpConfiguration; /// Used Kernel for ExaGeoStat Modeling Data. const kernels::Kernel *mpKernel; @@ -33,13 +31,10 @@ namespace exageostat::dataunits { * @brief Constructor. * @param aData The ExaGeoStatData object. * @param aConfiguration The Configurations object. - * @param aHardware The hardware configuration object. * @param aKernel The Kernel object. */ - mModelingData(std::unique_ptr> &aData, Configurations &aConfiguration, - const ExaGeoStatHardware &aHardware, T &aMatrix, const kernels::Kernel &aKernel) : - mpData(std::move(&aData)), mpConfiguration(&aConfiguration), mpHardware(&aHardware), - mpMeasurementsMatrix(&aMatrix), mpKernel(&aKernel) {} + mModelingData(std::unique_ptr> &aData, configurations::Configurations &aConfiguration, T &aMatrix, const kernels::Kernel &aKernel) : + mpData(std::move(&aData)), mpConfiguration(&aConfiguration), mpMeasurementsMatrix(&aMatrix), mpKernel(&aKernel) {} }; }//namespace exageostat diff --git a/inst/include/helpers/DiskWriter.hpp b/inst/include/helpers/DiskWriter.hpp deleted file mode 100644 index 7d43d834..00000000 --- a/inst/include/helpers/DiskWriter.hpp +++ /dev/null @@ -1,55 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file DiskWriter.hpp - * @brief Contains the definition of the DiskWriter class for writing data to disk. - * @version 1.1.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @date 2024-01-24 -**/ - -#ifndef EXAGEOSTATCPP_DISKWRITER_HPP -#define EXAGEOSTATCPP_DISKWRITER_HPP - -#include -#include - -namespace exageostat::helpers { - - /** - * @class DiskWriter - * @brief A class for writing data to disk. - * @tparam T Data Type: float or double - * - */ - template - class DiskWriter { - public: - - /** - * @brief Writes a matrix of vectors to disk. - * @param[in] aMatrixPointer A Reference to the matrix data. - * @param[in] aProblemSize The size of the problem. - * @param[in] aP The number of processes. - * @param[in] aLoggerPath The path to the logger file. - * @param[in] aLocations A Reference to the Locations object. - * @return void - * - */ - void static - WriteVectorsToDisk(const T &aMatrixPointer, const int &aProblemSize, const int &aP, std::string &aLoggerPath, - exageostat::dataunits::Locations &aLocations); - }; - - /** - * @brief Instantiates the DiskWriter class for float and double types. - * @tparam T Data Type: float or double - * - */ - EXAGEOSTAT_INSTANTIATE_CLASS(DiskWriter) -} -#endif //EXAGEOSTATCPP_DISKWRITER_HPP diff --git a/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp b/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp index 111e51ca..a62f861b 100644 --- a/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp +++ b/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp @@ -6,7 +6,7 @@ /** * @file UnivariatePowExpStationary.hpp * @brief Defines the UnivariatePowExpStationary class, a univariate stationary PowExp kernel. - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah * @author Suhas Shankar diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp index 85d57aad..347a55c3 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp @@ -9,7 +9,7 @@ * @version 1.1.0 * @author Mahmoud ElKarargy * @author Sameh Abdulah - * @date 2023-03-20 + * @date 2024-02-25 * @details This header file defines the abstract class LinearAlgebraMethods, which provides an interface for linear algebra solvers. * The purpose of this interface is to allow different concrete linear algebra solvers to be interchangeable, * so that they can be used interchangeably by other parts of the software system that rely on linear algebra. @@ -26,9 +26,9 @@ extern "C" { } #include -#include -#include +#include #include +#include #include namespace exageostat::linearAlgebra { @@ -59,7 +59,7 @@ namespace exageostat::linearAlgebra { * @return void * */ - void InitiateDescriptors(Configurations &aConfigurations, + void InitiateDescriptors(configurations::Configurations &aConfigurations, dataunits::DescriptorData &aDescriptorData, const int &aP, T *apMeasurementsMatrix = nullptr); @@ -67,8 +67,10 @@ namespace exageostat::linearAlgebra { * @brief Initializes the descriptors necessary for the Fisher prediction function. * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in,out] aDescriptorData Descriptor Data object to be populated with descriptors and data. + * @return void + * */ - void InitiateFisherDescriptors(Configurations &aConfigurations, + void InitiateFisherDescriptors(configurations::Configurations &aConfigurations, dataunits::DescriptorData &aDescriptorData); /** @@ -76,12 +78,11 @@ namespace exageostat::linearAlgebra { * @details This method initializes the descriptors necessary for the linear algebra solver. * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in,out] aData DescriptorData object to be populated with descriptors and data. - * @param[in] aP the P value of the kernel multiplied by time slot. * @return void * */ - void InitiatePredictionDescriptors(Configurations &aConfigurations, - std::unique_ptr> &aData, const int &aP); + void InitiatePredictionDescriptors(configurations::Configurations &aConfigurations, + std::unique_ptr> &aData); /** * @brief Initializes the descriptors necessary for the Prediction Auxiliary function MLE-MLOE-MMOM. @@ -92,53 +93,21 @@ namespace exageostat::linearAlgebra { * @return void * */ - void InitiateMLOEMMOMDescriptors(Configurations &aConfigurations, + void InitiateMLOEMMOMDescriptors(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, const int &aP); /** * @brief Generates synthetic data. * @param[in] aConfigurations The configurations object containing relevant settings. - * @param[in] aHardware ExaGeoStatHardware object representing the hardware. * @param[in,out] aData ExaGeoStatData object to be populated with synthetic data. * @param[in] aKernel Reference to the kernel object to use. - * @return None. + * @return void. * */ - void GenerateSyntheticData(Configurations &aConfigurations, - const ExaGeoStatHardware &aHardware, + void GenerateSyntheticData(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, const kernels::Kernel &aKernel); - /** - * @brief Computes the covariance matrix. - * @param[in] aDescriptorData pointer to the DescriptorData object holding descriptors and data. - * @param[out] apDescriptor Pointer to the descriptor for the covariance matrix. - * @param[in] aTriangularPart Specifies whether the upper or lower triangular part of the covariance matrix is stored. - * @param[in] apLocation1 Pointer to the first set of locations. - * @param[in] apLocation2 Pointer to the second set of locations. - * @param[in] apLocation3 Pointer to the third set of locations. - * @param[in] apLocalTheta Pointer to the local theta values. - * @param[in] aDistanceMetric Specifies the distance metric to use. - * @param[in] apKernel Pointer to the kernel object to use. - * @return void - * - */ - void CovarianceMatrixCodelet(dataunits::DescriptorData &aDescriptorData, void *apDescriptor, - const int &aTriangularPart, dataunits::Locations *apLocation1, - dataunits::Locations *apLocation2, dataunits::Locations *apLocation3, - T *apLocalTheta, const int &aDistanceMetric, const kernels::Kernel *apKernel); - - /** - * @brief Copies the descriptor data to a double vector. - * @param[in] aDescriptorData pointer to the DescriptorData object holding descriptors and data. - * @param[in] apDescriptor Pointer to the descriptor data. - * @param[in,out] apDoubleVector Pointer to the double vector to copy the descriptor data to. - * @return void - * - */ - void - CopyDescriptorZ(dataunits::DescriptorData &aDescriptorData, void *apDescriptor, T *apDoubleVector); - /** * @brief Generates the observations vector. * @param[in] aConfigurations Configurations object containing relevant settings. @@ -152,7 +121,7 @@ namespace exageostat::linearAlgebra { * */ void - GenerateObservationsVector(Configurations &aConfigurations, + GenerateObservationsVector(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, dataunits::Locations *apLocation1, dataunits::Locations *apLocation2, dataunits::Locations *apLocation3, const int &aDistanceMetric, @@ -160,7 +129,6 @@ namespace exageostat::linearAlgebra { /** * @brief Calculates the log likelihood value of a given value theta. - * @param[in] aHardware ExaGeoStatHardware object representing the hardware. * @param[in,out] aData DescriptorData object to be populated with descriptors and data. * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in] apTheta Optimization parameter used by NLOPT. @@ -169,19 +137,10 @@ namespace exageostat::linearAlgebra { * @return log likelihood value * */ - virtual T ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, - Configurations &aConfigurations, const double *apTheta, + virtual T ExaGeoStatMLETile(std::unique_ptr> &aData, + configurations::Configurations &aConfigurations, const double *apTheta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) = 0; - /** - * @brief Converts a Gaussian descriptor to a non-tiled descriptor. - * @param[in] aDescriptorData DescriptorData struct with the Gaussian descriptor. - * @param[in] apDesc Pointer to the non-tiled descriptor. - * @param[in] apTheta Theta vector. - * @return void - */ - void ExaGeoStatGaussianToNonTileAsync(dataunits::DescriptorData &aDescriptorData, void *apDesc, T *apTheta); /** * @brief Copies a matrix in the tile layout from source to destination @@ -193,33 +152,6 @@ namespace exageostat::linearAlgebra { */ virtual void ExaGeoStatLapackCopyTile(const common::UpperLower &aUpperLower, void *apA, void *apB) = 0; - /** - * @brief Initialize the runtime option structure for either HiCMA or CHAMELEON. - * @param[in, out] apOptions The options structure that needs to be initialized. - * @param[in] apContext The runtime context in which to initialize the runtime support. - * @param[in] apSequence The sequence structure to associate in the options. - * @param[in] apRequest The request structure to associate in the options. - * @return void - */ - virtual void - ExaGeoStatOptionsInit(void *apOptions, void *apContext, void *apSequence, void *apRequest) = 0; - - /** - * @brief Submit the release of the workspaces associated to the options structure. - * @param[in,out] apOptions The options structure for which to workspaces will be released - * @return void - */ - virtual void ExaGeoStatOptionsFree(void *apOptions) = 0; - - /** - * @brief Finalize the runtime option structure for either HiCMA or CHAMELEON. - * @param[in,out] apOptions The options structure that needs to be finalized. - * @param[in] apContext The runtime context in which to finalize the runtime support. - * @return void - * - */ - virtual void ExaGeoStatOptionsFinalize(void *apOptions, void *apContext) = 0; - /** * @brief Wait for the completion of a sequence. * @param[in] apSequence apSequence A pointer to either CHAMELEON or HiCMA sequence. @@ -270,30 +202,6 @@ namespace exageostat::linearAlgebra { const common::Trans &aTrans, const common::Diag &aDiag, const T &aAlpha, void *apA, void *apCD, void *apCrk, void *apZ, const int &aMaxRank) = 0; - /** - * @brief Calculate determinant for triangular matrix. - * @param[in] apDescA Pointer to the descriptor of the matrix 'descA'. - * @param[in] apSequence Pointer to a sequence structure for managing asynchronous execution. - * @param[in] apRequest Pointer to a request structure for tracking the operation's status. - * @param[out] apDescNum Pointer to the descriptor of the matrix to store the sum of elements. - * @param[out] apDescTrace Pointer to the descriptor of the matrix to store the trace. - * @return void - */ - void - ExaGeoStatMLETraceTileAsync(void *apDescA, void *apSequence, void *apRequest, void *apDescNum, - void *apDescTrace); - - /** - * @brief Calculate determinant for triangular matrix. - * @param[in] apDescA Exageostat descriptor. - * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. - * @param[in] apRequest Identifies this function call (for exception handling purposes). - * @param[in] apDescDet determinant value - * @return Returns 0 for success, error code otherwise. - * - */ - virtual int - ExaGeoStatMeasureDetTileAsync(void *apDescA, void *apSequence, void *apRequest, void *apDescDet) = 0; /** * @brief Solve a positive definite linear system of equations AX = B using tiled algorithms. @@ -305,19 +213,7 @@ namespace exageostat::linearAlgebra { void ExaGeoStatPosvTile(const common::UpperLower &aUpperLower, void *apA, void *apB); /** - * @brief Calculate mean square prediction error (MSPE) scalar value of the prediction. - * @param[in] apDescZPredict Observed measurements. - * @param[in] apDescZMiss Missing measurements. - * @param[out] apDescError Mean Square Prediction Error (MSPE). - * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. - * @param[out] apRequest Identifies this function call (for exception handling purposes). - * @return Returns 0 for success, error code otherwise. - */ - int ExaGeoStatMLEMSPETileAsync(void *apDescZPredict, void *apDescZMiss, void *apDescError, void *apSequence, - void *apRequest); - - /** - * Predict missing values base on a set of given values and covariance matrix/ + * @brief Predict missing values base on a set of given values and covariance matrix/ * @param[in] aData Reference to Data containing different MLE inputs. * @param[in] apTheta theta Vector with three parameter (Variance, Range, Smoothness) that is used to to generate the Covariance Matrix. * @param[in] aZMissNumber number of missing values (unknown observations). @@ -325,7 +221,6 @@ namespace exageostat::linearAlgebra { * @param[in] apZObs observed values vector (known observations). * @param[in] apZActual actual missing values vector (in the case of testing MSPE). * @param[in] apZMiss missing values vector (unknown observations). - * @param[in] aHardware ExaGeoStatHardware object representing the hardware. * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in] aMissLocations Reference to Locations object containing missed locations. * @param[in] aObsLocations Reference to Locations object containing observed locations. @@ -335,14 +230,13 @@ namespace exageostat::linearAlgebra { T *ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const ExaGeoStatHardware &aHardware, - Configurations &aConfiguration, + configurations::Configurations &aConfiguration, exageostat::dataunits::Locations &aMissLocations, exageostat::dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); /** - * Predict missing values base on a set of given values and Non-Gaussian covariance matrix/ + * @brief Predict missing values base on a set of given values and Non-Gaussian covariance matrix/ * @param[in] aData Reference to Data containing different MLE inputs. * @param[in] apTheta theta Vector with three parameter (Variance, Range, Smoothness) that is used to to generate the Covariance Matrix. * @param[in] aZMissNumber number of missing values (unknown observations). @@ -350,7 +244,6 @@ namespace exageostat::linearAlgebra { * @param[in] apZObs observed values vector (known observations). * @param[in] apZActual actual missing values vector (in the case of testing MSPE). * @param[in] apZMiss missing values vector (unknown observations). - * @param[in] aHardware ExaGeoStatHardware object representing the hardware. * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in] aMissLocations Reference to Locations object containing missed locations. * @param[in] aObsLocations Reference to Locations object containing observed locations. @@ -360,8 +253,7 @@ namespace exageostat::linearAlgebra { T *ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const ExaGeoStatHardware &aHardware, - Configurations &aConfiguration, + configurations::Configurations &aConfiguration, exageostat::dataunits::Locations &aMissLocations, exageostat::dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); @@ -417,8 +309,10 @@ namespace exageostat::linearAlgebra { * @param[in] aSize Size of the matrix. * @param[in] aDescData Descriptor data containing required Z matrix Descriptor. * @param[in] aP the P value of the kernel multiplied by time slot. + * @return void + * */ - void ExaGeoStatGetZObs(Configurations &aConfigurations, T *apZ, const int &aSize, + void ExaGeoStatGetZObs(configurations::Configurations &aConfigurations, T *apZ, const int &aSize, exageostat::dataunits::DescriptorData &aDescData, T *apMeasurementsMatrix, const int &aP); /** @@ -427,7 +321,6 @@ namespace exageostat::linearAlgebra { * maximum likelihood on the empirical orthogonal functions (MLOE), and method of moments (MMOM). * @param[in] aConfigurations Configurations for the prediction. * @param[in, out] aData Data for prediction (input and output). - * @param[in] aHardware Hardware specifications for the prediction. * @param[in] apTruthTheta Pointer to the true theta values. * @param[in] apEstimatedTheta Pointer to the estimated theta values. * @param[in] aMissLocations Locations of missing values. @@ -435,43 +328,23 @@ namespace exageostat::linearAlgebra { * @param[in] aKernel Reference to the kernel object to use. * @return void */ - void ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigurations, + void ExaGeoStatMLETileMLOEMMOM(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, - const ExaGeoStatHardware &aHardware, T *apTruthTheta, - T *apEstimatedTheta, dataunits::Locations &aMissLocations, + T *apTruthTheta, T *apEstimatedTheta, dataunits::Locations &aMissLocations, dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel); /** * @brief Maximum Likelihood Evaluation (MLE) Fisher method. * @param[in] aConfigurations Configurations object containing relevant settings. * @param[in,out] aData Descriptor Data object to be populated with descriptors and data. - * @param[in] aHardware ExaGeoStatHardware object representing the hardware. * @param[in] apTheta Pointer containing three parameter (Variance, Range, Smoothness) that is used to to generate the Covariance Matrix. * @param[in] aKernel Reference to the kernel object to use. * @return Fisher Matrix */ T * - ExaGeoStatFisherTile(Configurations &aConfigurations, + ExaGeoStatFisherTile(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, - const ExaGeoStatHardware &aHardware, T *apTheta, - const kernels::Kernel &aKernel); - - /** - * @brief Perform an asynchronous computation of MLE, MLOE, and MMOM for a tile. - * @details his function performs the computation of Maximum Likelihood Estimation (MLE), - * Maximum Likelihood on the Empirical Orthogonal Functions (MLOE), and - * Method of Moments (MMOM) for a tile asynchronously. - * @param[in] apDescExpr2 Descriptor for expression 2. - * @param[in] apDescExpr3 Descriptor for expression 3. - * @param[in] apDescExpr4 Descriptor for expression 4. - * @param[in] apDescMLOE Descriptor for MLOE. - * @param[in] apDescMMOM Descriptor for MMOM. - * @param[in] apSequence Sequence for the computation. - * @param[in] apRequest Request for the computation. - * @return Result of the asynchronous operation. - */ - int ExaGeoStatMLETileAsyncMLOEMMOM(void *apDescExpr2, void *apDescExpr3, void *apDescExpr4, void *apDescMLOE, - void *apDescMMOM, void *apSequence, void *apRequest); + T *apTheta, const kernels::Kernel &aKernel); /** * @brief Perform a matrix addition with scaling. @@ -502,643 +375,16 @@ namespace exageostat::linearAlgebra { void *apDescB); /** - * @brief Get the pointer to the data or the runtime handler associated to the piece of data (m, n) in desc. - * @param[in] apA The descriptor to which belongs the piece of data - * @param[in] aAm The row coordinate of the piece of data in the matrix - * @param[in] aAn he column coordinate of the piece of data in the matrix - * @return The runtime handler address of the piece of data. - */ - virtual void *ExaGeoStatDataGetAddr(void *apA, int aAm, int aAn) = 0; - - /** - * @brief Calculate mean square error (MSE) scalar value for Bivariate kernels. - * @param[in] apDescZPre Observed measurements descZpre. - * @param[in] apDescZMiss Missing measurements descZpre - * @param[out] apDescsError1 Mean Square Error (MSE) 1. - * @param[out] apDescsError2 Mean Square Error (MSE) 2. - * @param[out] apDescsError Mean Square Error (MSE). - * @param[in] apSequence Sequence for the computation. - * @param[in] apRequest Request for the computation. - * @return successful exit - */ - int ExaGeoStatMLEMSPEBivariateTileAsync(void *apDescZPre, void *apDescZMiss, void *apDescsError1, - void *apDescsError2, void *apDescsError, - void *apSequence, void *apRequest); - - /** - * @brief Transform the measurements vector inside the non-Gaussian MLE function. - * @param[in] apDescZ pointer to the Observed Measurements descriptor. - * @param[in] apDescFlag Pointer to flag descriptor. - * @param[in] apTheta Pointer to Model parameters. - * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. - * @param[in] apRequest Identifies this function call (for exception handling purposes). - * @return Returns 0 for success, error code otherwise. - */ - virtual int - ExaGeoStatNonGaussianTransformTileAsync(void *apDescZ, void *apDescFlag, const T *apTheta, void *apSequence, - void *apRequest) = 0; - - /** - * @brief Calculate the log likelihood of non-Gaussian MLE. - * @param[in] apDescZ pointer to the Observed Measurements descriptor. - * @param[in] apDescSum The log-likelihood Sum of descriptor Z. - * @param[in] apTheta Pointer to Model parameters. - * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. - * @param[out] apRequest Identifies this function call (for exception handling purposes). - * @return Returns 0 for success, error code otherwise. - */ - virtual int - ExaGeoStatNonGaussianLogLikeTileAsync(void *apDescZ, void *apDescSum, const T *apTheta, void *apSequence, - void *apRequest) = 0; - - /** - * @brief Sets the context. - * @param[in] apContext The context. + * @brief Recovers theta and log-likelihood from a file. + * @param[in] apPath A pointer to the path of the file from which to recover the data. + * @param[in] aIterationCount The iteration count to look for in the file. + * @param[in,out] apTheta A pointer to the array where the theta values will be stored. + * @param[in,out] apLogLik A pointer to the variable where the log-likelihood value will be stored. + * @param[in] aNumParams The number of parameters (elements) in the theta array. + * @return bool `true` if the specified iteration count is found and successfully parsed, `false` otherwise. * */ - void SetContext(void *apContext) { - this->mpContext = apContext; - } - - //// TODO: Create a Factory for Runtime system. HiCMAPP Model will be applied. - struct starpu_codelet cl_gaussian_to_non = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_gaussian_to_non_starpu}, - .nbuffers = 1, - .modes = {STARPU_RW}, - .name = "gaussian_to_non" - }; - - struct starpu_codelet cl_dzcpy = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_dzcpy_Starpu}, - .nbuffers = 1, - .modes = {STARPU_W}, - .name = "dzcpy" - }; - - struct starpu_codelet cl_dmdet = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_dmdet_starpu}, - .nbuffers = 2, - .modes = {STARPU_R, STARPU_RW}, - .name = "dmdet" - }; - - struct starpu_codelet cl_stride_vec = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_stride_vector_starpu}, - .nbuffers = 3, - .modes = {STARPU_R, STARPU_W, STARPU_W}, - .name = "stride_vec" - }; - - struct starpu_codelet cl_tri_stride_vec = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_tri_stride_vector_starpu}, - .nbuffers = 4, - .modes = {STARPU_R, STARPU_W, STARPU_W, STARPU_W}, - .name = "tri_stride_vec" - }; - - struct starpu_codelet cl_dmse = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_dmse_starpu}, - .nbuffers = 3, - .modes = {STARPU_RW, STARPU_R, STARPU_R}, - .name = "dmse" - }; - - struct starpu_codelet cl_dmloe_mmom = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_dmloe_mmom_starpu}, - .nbuffers = 5, - .modes = {STARPU_R, STARPU_R, STARPU_R, STARPU_RW, STARPU_RW}, - .name = "dmloe_mmom" - }; - - struct starpu_codelet cl_dcmg = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_dcmg_starpu}, - .nbuffers = 1, - .modes = {STARPU_W}, - .name = "dcmg" - }; - - struct starpu_codelet cl_dtrace = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_dtrace_starpu}, - .nbuffers = 3, - .modes = {STARPU_R, STARPU_RW, STARPU_W}, - .name = "dtrace" - }; - - struct starpu_codelet cl_ddotp = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_ddotp_starpu}, - .nbuffers = 2, - .modes = {STARPU_RW, STARPU_R}, - .name = "ddotp" - }; - - struct starpu_codelet cl_dmse_bivariate = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_dmse_bivariate_starpu}, - .nbuffers = 5, - .modes = {STARPU_RW, STARPU_RW, STARPU_RW, STARPU_R, STARPU_R}, - .name = "dmse_bivariate" - }; - struct starpu_codelet cl_non_gaussian_transform = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_non_gaussian_transform_starpu}, - .nbuffers = 2, - .modes = {STARPU_RW, STARPU_W}, - .name = "non_gaussian_transform" - }; - - struct starpu_codelet cl_non_gaussian_loglike = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_non_gaussian_loglike_starpu}, - .nbuffers = 2, - .modes = {STARPU_R, STARPU_RW}, //Read access to Z and Read/Write access to the sum. - .name = "non_gaussian_loglike" - }; - - struct starpu_codelet cl_non_gaussian_loglike_lr = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_non_gaussian_loglike_lr_starpu}, - .nbuffers = 2, - .modes = {STARPU_R, STARPU_RW}, //Read access to Z and Read/Write access to the sum. - .name = "non_gaussian_loglike_lr" - }; - - struct starpu_codelet cl_non_gaussian_transform_lr = - { - .where = STARPU_CPU, - .cpu_funcs = {CORE_non_gaussian_transform_lr_starpu}, - .nbuffers = 1, - .modes = {STARPU_RW}, - .name = "non_gaussian_transform_lr" - }; - - static void CORE_dcmg_starpu(void *apBuffers[], void *apCodeletArguments) { - int m, n, m0, n0; - exageostat::dataunits::Locations *location1; - exageostat::dataunits::Locations *location2; - exageostat::dataunits::Locations *location3; - T *pTheta; - T *pA; - int distance_metric; - kernels::Kernel *pKernel; - - pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - starpu_codelet_unpack_args(apCodeletArguments, &m, &n, &m0, &n0, &location1, &location2, &location3, - &pTheta, &distance_metric, &pKernel); - pKernel->GenerateCovarianceMatrix(pA, m, n, m0, n0, *location1, *location2, *location3, pTheta, - distance_metric); - } - - static void CORE_gaussian_to_non_starpu(void *apBuffers[], void *apCodeletArguments) { - int m, m0; - T *pZ; - T *pTheta; - pTheta = new T[6]; - pZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, &pTheta[0], &pTheta[1], &pTheta[2], &pTheta[3], - &pTheta[4], &pTheta[5]); - //core function to convert Z tile from Gaussian to non-Gaussian. - core_gaussian_to_non(pZ, pTheta, m); - delete[] pTheta; - } - - static void CORE_dzcpy_Starpu(void *apBuffers[], void *apCodeletArguments) { - int m; - T *pA; - int m0; - T *pR; - - pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, &pR); - memcpy(pA, &pR[m0], m * sizeof(T)); - } - - static void CORE_dmdet_starpu(void *apBuffers[], void *apCodeletArguments) { - int m; - T *pA; - T det = 0; - T *pDeterminant = &det; - - *pDeterminant = 0; - pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pDeterminant = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - starpu_codelet_unpack_args(apCodeletArguments, &m); - T local_det = Core_dmdet(pA, m); - *pDeterminant += local_det; - } - - static void CORE_stride_vector_starpu(void *apBuffers[], void *apCodeletArguments) { - int m; - int temp; - T *pA; - T *pB; - T *pC; - int m0; - int i; - int j = 0; - - pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pB = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - pC = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); - starpu_codelet_unpack_args(apCodeletArguments, &temp, &m0, &m); - - for (i = 0; i < temp - 1; i += 2) { - pB[j] = pA[i]; - pC[j] = pA[i + 1]; - j++; - } - } - - static void CORE_tri_stride_vector_starpu(void *apBuffers[], void *apCodeletArguments) { - int m; - int temp; - T *pA; - T *pB; - T *pC; - T *pD; - int m0; - int i; - int j; - - pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pB = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - pC = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); - pD = (T *) STARPU_MATRIX_GET_PTR(apBuffers[3]); - - starpu_codelet_unpack_args(apCodeletArguments, &temp, &m0, &m); - - //accept only temp divided by three (should be optimized) - j = 0; - for (i = 0; i < temp - 1; i += 3) { - pB[j] = pA[i]; - pC[j] = pA[i + 1]; - pD[j] = pA[i + 2]; - j++; - } - } - - static void CORE_dmse_starpu(void *apBuffers[], void *apCodeletArguments) { - int m, m0, i; - T *pZPredict; - T *pZMiss; - T *pError; - T local_error = 0.0; - - pError = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pZPredict = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - pZMiss = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); - - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0); - for (i = 0; i < m; i++) { - local_error += pow((pZPredict[i] - pZMiss[i]), 2); - } - *pError += local_error; - } - - static void CORE_dmloe_mmom_starpu(void *apBuffers[], void *apCodeletArguments) { - int m; - int n; - int i; - T *pExpr2; - T *pExpr3; - T *pExpr4; - T *pMloe; - T *pMmom; - int m0; - int n0; - - pExpr2 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pExpr3 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - pExpr4 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); - pMloe = (T *) STARPU_MATRIX_GET_PTR(apBuffers[3]); - pMmom = (T *) STARPU_MATRIX_GET_PTR(apBuffers[4]); - - starpu_codelet_unpack_args(apCodeletArguments, &m, &n, &m0, &n0); - T expr2_ = 0, expr3_ = 0, expr4_ = 0; - - for (i = 0; i < m * n; i += 2) { - expr2_ += pExpr2[i]; - expr3_ += pExpr3[i]; - expr4_ += pExpr4[i]; - } - - if (expr3_ == 0.0) { - *pMloe -= 1.0; - } else { - *pMloe += (expr2_ / expr3_) - 1.0; - } - - if (expr2_ == 0.0) { - *pMmom -= 1.0; - } else { - *pMmom += (expr4_ / expr2_) - 1.0; - } - } - - static void CORE_dtrace_starpu(void *apBuffers[], void *apCodeletArguments) { - - int m; - T *pA; - T s = 0; - T *pSum = &s; - T *pTrace; - - *pSum = 0; - pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pSum = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - pTrace = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); - starpu_codelet_unpack_args(apCodeletArguments, &m); - - T local_s = core_dtrace(pA, m, pTrace); - *pSum += local_s; - } - - static void CORE_ddotp_starpu(void *apBuffers[], void *apCodeletArguments) { - int m, m0; - T *pA; - T *pDot_product; - - pDot_product = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0); - T local_dot = cblas_ddot(m, (double *) pA, 1, (double *) pA, 1); - *pDot_product += local_dot; - } - - static void CORE_non_gaussian_transform_starpu(void *apBuffers[], void *apCodeletArguments) { - int m, m0; - T *pZ; - T *pTheta; - T flag = 0; - T *pAll_flags = &flag; - *pAll_flags = 0; - - pTheta = new T[6]; - pZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, /*&flag,*/ - &pTheta[0], &pTheta[1], &pTheta[2], - &pTheta[3], &pTheta[4], &pTheta[5]); - - //Transform the measurements vector inside the non-Gaussian MLE function. - core_non_gaussian_transform(pZ, pTheta, m); - delete[] pTheta; - } - - static void CORE_non_gaussian_loglike_starpu(void *apBuffers[], void *apCodeletArguments) { - int m, m0; - T *pZ; - T sum = 0; - T *pA = ∑ - - *pA = 0; - auto *pTheta = new T[6]; - pZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pA = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, - &pTheta[0], &pTheta[1], &pTheta[2], - &pTheta[3], &pTheta[4], &pTheta[5]); - - T local_sum = core_non_gaussian_loglike(pZ, pTheta, m); - *pA += local_sum; - delete[] pTheta; - } - - static void CORE_non_gaussian_loglike_lr_starpu(void *apBuffers[], void *apCodeletArguments) { - int m, m0; - T *pZ; - T *pTheta; - T sum; - T *pS; - - pTheta = (T *) new T[6]; - pZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pS = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, - &pTheta[0], &pTheta[1], &pTheta[2], - &pTheta[3], &pTheta[4], &pTheta[5]); - - T local_sum = core_non_gaussian_loglike(pZ, pTheta, m); - *pS += local_sum; - } - - static void CORE_non_gaussian_transform_lr_starpu(void *apBuffers[], void *apCodeletArguments) { - int m, m0; - T *pZ; - T *pTheta; - T flag = 0; - T *pAll_flags = &flag; - *pAll_flags = 0; - - pTheta = (T *) new T[6]; - pZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0, /*&flag,*/ - &pTheta[0], &pTheta[1], &pTheta[2], - &pTheta[3], &pTheta[4], &pTheta[5]); - - //Transform the measurements vector inside the non-Gaussian MLE function. - core_non_gaussian_transform(pZ, pTheta, m); - } - - static void core_gaussian_to_non(T *pZ, T *apLocalTheta, int aSize) { - - T xi = apLocalTheta[2]; - T omega = apLocalTheta[3]; - T g = apLocalTheta[4]; - T h = apLocalTheta[5]; - - int i; - if (h < 0) { - throw std::runtime_error("The kurtosis parameter cannot be negative"); - } - if (g == 0) { - for (i = 0; i < aSize; i++) - pZ[i] = xi + omega * pZ[i] * (exp(0.5 * h * pow(pZ[i], 2))); - } else { - for (i = 0; i < aSize; i++) - pZ[i] = xi + omega * (exp(g * pZ[i]) - 1) * (exp(0.5 * h * pow(pZ[i], 2))) / g; - } - } - - static T Core_dmdet(T *apA, int aSize) { - - int i; - T res = 0.0; - for (i = 0; i < aSize; i++) { - if (apA[i + i * aSize] > 0) - res += log(apA[i + i * aSize]); - } - return res; - } - - static double core_dtrace(T *pA, int m, T *pTrace) { - int i; - T res = 0.0; - for (i = 0; i < m; i++) { - res += pA[i + i * m]; - pTrace[i] = pA[i + i * m]; - } - return res; - } - - static double tukeyGHTransfor(T aZ_non, T aZ, T aXi, T aOmega, T aG, T aH) { - if (aG == 0) - return aZ_non - aXi - aOmega * aZ * exp(0.5 * aH * aZ * aZ); - else - return aZ_non - aXi - (aOmega * (exp(aG * aZ) - 1) * (exp(0.5 * aH * aZ * aZ)) / aG); - } - - static double tukeyGHDiferencial(T aZ, T aOmega, T aG, T aH) { - if (aG == 0) - return -aOmega * exp((aH * aZ * aZ) / 2.0) - aOmega * aH * aZ * aZ * exp((aH * aZ * aZ) / 2.0); - else - return -aOmega * exp(aG * aZ) * exp((aH * aZ * aZ) / 2.0) - - (aH * aZ * exp((aH * aZ * aZ) / 2.0) * (aOmega * exp(aG * aZ) - aOmega)) / aG; - } - - static double newton_raphson(T aZ, T aXi, T aOmega, T aG, T aH, T aEps) { - int itr, max_itr; - T x0, x1, all_err; - x0 = 0; - T diff; - all_err = aEps; - max_itr = 1000; - for (itr = 1; itr <= max_itr; itr++) { - diff = tukeyGHTransfor(aZ, x0, aXi, aOmega, aG, aH) / tukeyGHDiferencial(x0, aOmega, aG, aH); - x1 = x0 - diff; - if (fabs(diff) < all_err) - return x1; - x0 = x1; - } - - return x1; - } - - static void core_non_gaussian_transform(T *apZ, T *apLocalTheta, int m) { - - T xi = apLocalTheta[2]; - T omega = apLocalTheta[3]; - T g = apLocalTheta[4]; - T h = apLocalTheta[5]; - - T eps = 1.0e-5; - for (int i = 0; i < m; i++) - apZ[i] = newton_raphson(apZ[i], xi, omega, g, h, eps); - } - - static double core_non_gaussian_loglike(T *apZ, T *apLocalTheta, int m) { - T g = apLocalTheta[4]; - T h = apLocalTheta[5]; - - int i; - T sum = 0.0; - if (h < 0) { - throw std::runtime_error("The kurtosis parameter cannot be negative"); - - } - for (i = 0; i < m; i++) { - if (g == 0) - sum += log(1 + h * pow(apZ[i], 2)) + 0.5 * h * pow(apZ[i], 2); - else { - sum += log(exp(g * apZ[i]) + (exp(g * apZ[i]) - 1) * h * apZ[i] / g) + 0.5 * h * pow(apZ[i], 2); - } - } - return sum; - } - - static void CORE_dmse_bivariate_starpu(void *apBuffers[], void *apCodeletArguments) { - int m, m0, i; - T *pZpre; - T *pZmiss; - T *pSerror1; - T *pSerror2; - T *pSerror; - T local_serror1 = 0.0; - T local_serror2 = 0.0; - T local_serror = 0.0; - - pSerror1 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - pSerror2 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); - pSerror = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); - pZpre = (T *) STARPU_MATRIX_GET_PTR(apBuffers[3]); - pZmiss = (T *) STARPU_MATRIX_GET_PTR(apBuffers[4]); - - starpu_codelet_unpack_args(apCodeletArguments, &m, &m0); - - for (i = 0; i < m; i++) { - if (i % 2 == 0) { - local_serror1 += pow((pZpre[i] - pZmiss[i]), 2); - } else - local_serror2 += pow((pZpre[i] - pZmiss[i]), 2); - local_serror += pow((pZpre[i] - pZmiss[i]), 2); - } - *pSerror1 += local_serror1; - *pSerror2 += local_serror2; - *pSerror += local_serror; - } - - bool recover(char *apPath, int aIterationCount, T *apTheta, T *apLogLik, int aNumParams) { - - FILE *fp; - char *line = nullptr; - size_t len = 0; - int count; - int i; - char *pch; - fp = fopen(apPath, "r"); - if (fp == nullptr) { - throw std::runtime_error("Cannot open observations file"); - } - while (getline(&line, &len, fp) != -1) { - pch = strtok(line, " "); - count = (int) strtol(pch, nullptr, 10); - if (count == aIterationCount) { - pch = strtok(nullptr, " "); - for (i = 0; i < aNumParams; i++) { - apTheta[i] = strtol(pch, nullptr, 10); - pch = strtok(nullptr, " "); - } - *apLogLik = strtol(pch, nullptr, 10); - fclose(fp); - free(line); - return true; - } - } - - fclose(fp); - free(line); - return false; - } - - protected: - /// Used Context. - void *mpContext = nullptr; + bool Recover(char *apPath, const int &aIterationCount, T *apTheta, T *apLogLik, const int &aNumParams); }; /** diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp index 84fd332f..994f1a06 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp @@ -33,8 +33,8 @@ namespace exageostat::linearAlgebra { * @brief Calculates the log likelihood value of a given value theta. * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() */ - T ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, - Configurations &aConfigurations, const double *theta, + T ExaGeoStatMLETile(std::unique_ptr> &aData, + configurations::Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) override; /** @@ -43,12 +43,6 @@ namespace exageostat::linearAlgebra { */ void ExaGeoStatLapackCopyTile(const common::UpperLower &aUpperLower, void *apA, void *apB) override; - /** - * @brief Get the pointer to the data or the runtime handler associated to the piece of data (m, n) in desc. - * @copydoc LinearAlgebraMethods::ExaGeoStatDataGetAddr() - */ - void *ExaGeoStatDataGetAddr(void *apA, int aAm, int aAn) override; - /** * @brief Solves one of the matrix equations op( A )*X = alpha*B, or X*op( A ) = alpha*B. * @copydoc LinearAlgebraMethods::ExaGeoStatTrsmTile() @@ -57,13 +51,6 @@ namespace exageostat::linearAlgebra { const common::Trans &aTrans, const common::Diag &aDiag, const T &aAlpha, void *apA, void *apCD, void *apCrk, void *apZ, const int &aMaxRank) override; - /** - * @brief Calculate determinant for triangular matrix. - * @copydoc LinearAlgebraMethods::ExaGeoStatMeasureDetTileAsync() - */ - int - ExaGeoStatMeasureDetTileAsync(void *apDescA, void *apSequence, void *apRequest, void *apDescDet) override; - /** * @brief Wait for the completion of a sequence. * @copydoc LinearAlgebraMethods::ExaGeoStatSequenceWait() @@ -77,82 +64,6 @@ namespace exageostat::linearAlgebra { */ void ExaGeoStatCreateSequence(void *apSequence) override; - - /** - * @brief Initialize the runtime option structure for CHAMELEON - * @copydoc LinearAlgebraMethods::ExaGeoStatOptionsInit() - */ - void - ExaGeoStatOptionsInit(void *apOptions, void *apContext, void *apSequence, void *apRequest) override; - - /** - * @brief Submit the release of the workspaces associated to the options structure. - * @copydoc LinearAlgebraMethods::ExaGeoStatOptionsFree() - */ - void - ExaGeoStatOptionsFree(void *apOptions) override; - - /** - * @brief Finalize the runtime option structure for CHAMELEON. - * @copydoc LinearAlgebraMethods::ExaGeoStatOptionsFinalize() - */ - void - ExaGeoStatOptionsFinalize(void *apOptions, void *apContext) override; - - /** - * @brief copy Chameleon descriptor to vector float*. - * @param[in] apDescA Exageostat descriptor A. - * @param[in] apDescB Exageostat descriptor B. - * @param[in] apDescC Exageostat descriptor C. - * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. - * @param[in] apRequest Identifies this function call (for exception handling purposes). - * @return Returns 0 for success, error code otherwise. - * - */ - int - ExaGeoStaStrideVectorTileAsync(void *apDescA, void *apDescB, void *apDescC, void *apSequence, void *apRequest); - - /** - * @brief Copy Chameleon descriptor to vector float*. - * @param[in] apDescA Exageostat descriptor A. - * @param[in] apDescB Exageostat descriptor B. - * @param[in] apDescC Exageostat descriptor C. - * @param[in] apDescD Exageostat descriptor D. - * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. - * @param[in] apRequest Identifies this function call (for exception handling purposes). - * @return Returns 0 for success, error code otherwise. - */ - int - ExaGeoStaStrideVectorTileAsync(void *apDescA, void *apDescB, void *apDescC, void *apDescD, void *apSequence, - void *apRequest); - - /** - * @brief Computes dot product of A.A. - * @param[in] apDescA A Descriptor - * @param[out] apDescProduct Stores the result of A.A. - * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. - * @param[in] apRequest Identifies this function call (for exception handling purposes). - * @return Returns 0 for success, error code otherwise. - */ - int - ExaGeoStatDoubleDotProduct(void *apDescA, void *apDescProduct, void *apSequence, void *apRequest); - - /** - * @brief Calculate the loglikelihood of non-Gaussian MLE. - * @copydoc LinearAlgebraMethods::ExaGeoStatNonGaussianLogLikeTileAsync() - */ - int - ExaGeoStatNonGaussianLogLikeTileAsync(void *apDescZ, void *apDescSum, const T *apTheta, void *apSequence, - void *apRequest) override; - - /** - * @brief Calculate the loglikelihood of non-Gaussian MLE. - * @copydoc LinearAlgebraMethods::ExaGeoStatNonGaussianLogLikeTileAsync() - */ - int - ExaGeoStatNonGaussianTransformTileAsync(void *apDescZ, void *apDescFlag, const T *apTheta, void *apSequence, - void *apRequest) override; - }; EXAGEOSTAT_INSTANTIATE_CLASS(ChameleonImplementation) diff --git a/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp index dfa3594f..29bef95b 100644 --- a/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp @@ -46,15 +46,14 @@ namespace exageostat::linearAlgebra::tileLowRank { * @param[in] aP the P value of the kernel multiplied by time slot. */ void SetModelingDescriptors(std::unique_ptr> &aData, - Configurations &aConfigurations, const int &aP); + configurations::Configurations &aConfigurations, const int &aP); /** * @brief Calculates the log likelihood value of a given value theta. * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() */ - T ExaGeoStatMLETile(const ExaGeoStatHardware &apHardware, - std::unique_ptr> &aData, - Configurations &aConfigurations, const double *theta, + T ExaGeoStatMLETile(std::unique_ptr> &aData, + configurations::Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) override; /** @@ -63,25 +62,6 @@ namespace exageostat::linearAlgebra::tileLowRank { */ void ExaGeoStatLapackCopyTile(const common::UpperLower &aUpperLower, void *apA, void *apB) override; - /** - * @brief Initialize the runtime option structure for HiCMA - * @copydoc LinearAlgebraMethods::ExaGeoStatOptionsInit() - */ - void - ExaGeoStatOptionsInit(void *apOptions, void *apContext, void *apSequence, void *apRequest) override; - - /** - * @brief Submit the release of the workspaces associated to the options structure. - * @copydoc LinearAlgebraMethods::ExaGeoStatOptionsFree() - */ - void ExaGeoStatOptionsFree(void *apOptions) override; - - /** - * @brief Finalize the runtime option structure for HiCMA. - * @copydoc LinearAlgebraMethods::ExaGeoStatOptionsFinalize() - */ - void ExaGeoStatOptionsFinalize(void *apOptions, void *apContext) override; - /** * @brief Wait for the completion of a sequence. * @copydoc LinearAlgebraMethods::ExaGeoStatSequenceWait() @@ -111,31 +91,6 @@ namespace exageostat::linearAlgebra::tileLowRank { const common::Trans &aTrans, const common::Diag &aDiag, const T &aAlpha, void *apA, void *apCD, void *apCrk, void *apZ, const int &aMaxRank) override; - /** - * @brief Calculate determinant for triangular matrix. - * @copydoc LinearAlgebraMethods::ExaGeoStatMeasureDetTileAsync() - */ - int ExaGeoStatMeasureDetTileAsync(void *apDescA, void *apSequence, void *apRequest, void *apDescDet) override; - - /** - * @brief Get the pointer to the data or the runtime handler associated to the piece of data (m, n) in desc. - * @copydoc LinearAlgebraMethods::ExaGeoStatDataGetAddr() - */ - void *ExaGeoStatDataGetAddr(void *apA, int aAm, int aAn) override; - - /** - * @brief Calculate the loglikelihood of non-Gaussian MLE. - * @copydoc LinearAlgebraMethods::ExaGeoStatNonGaussianLogLikeTileAsync() - */ - int ExaGeoStatNonGaussianLogLikeTileAsync(void *apDescZ, void *apDescSum, const T *apTheta, - void *apSequence, void *apRequest) override; - - /** - * @brief Calculate the loglikelihood of non-Gaussian MLE. - * @copydoc LinearAlgebraMethods::ExaGeoStatNonGaussianTransformTileAsync() - */ - int ExaGeoStatNonGaussianTransformTileAsync(void *apDescZ, void *apDescFlag, const T *apTheta, - void *apSequence, void *apRequest) override; }; /** diff --git a/inst/include/prediction/Prediction.hpp b/inst/include/prediction/Prediction.hpp index d880f125..ae87ddad 100644 --- a/inst/include/prediction/Prediction.hpp +++ b/inst/include/prediction/Prediction.hpp @@ -31,17 +31,16 @@ namespace exageostat::prediction { /** * @brief Takes care of calling the MSPE function, and the appropriate auxiliary function. - * @param[in] aHardware Reference to Hardware configuration for the ExaGeoStat solver. * @param[in, out] aData Reference to an ExaGeoStatData object containing needed descriptors, and locations. * @param[in] aConfigurations Reference to Configurations object containing user input data. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. * @param[in] aKernel Reference to the kernel object to use. * @return */ - static void PredictMissingData(const ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, - Configurations &aConfigurations, - T *apMeasurementsMatrix, const kernels::Kernel &aKernel); + static void PredictMissingData(std::unique_ptr> &aData, configurations::Configurations &aConfigurations, + T *apMeasurementsMatrix, const kernels::Kernel &aKernel, + dataunits::Locations *apTrainLocations = nullptr, + dataunits::Locations *apTestLocations = nullptr); /** * @brief Initializes needed pointers for prediction. @@ -56,13 +55,13 @@ namespace exageostat::prediction { * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ - static void InitializePredictionArguments(Configurations &aConfigurations, - std::unique_ptr> &aData, - std::unique_ptr> &aLinearAlgebraSolver, - T *apZObs, T *apZActual, - exageostat::dataunits::Locations &aMissLocation, - exageostat::dataunits::Locations &aObsLocation, - T *apMeasurementsMatrix, const int &aP); + static void + InitializePredictionArguments(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, + std::unique_ptr> &aLinearAlgebraSolver, + T *apZObs, T *apZActual, exageostat::dataunits::Locations &aMissLocation, + exageostat::dataunits::Locations &aObsLocation, T *apMeasurementsMatrix, + const int &aP, dataunits::Locations *apTrainLocations, + dataunits::Locations *apTestLocations); }; diff --git a/inst/include/prediction/PredictionHelpers.hpp b/inst/include/prediction/PredictionHelpers.hpp index c1fdbe02..195a5e9c 100644 --- a/inst/include/prediction/PredictionHelpers.hpp +++ b/inst/include/prediction/PredictionHelpers.hpp @@ -41,7 +41,7 @@ namespace exageostat::prediction { * @param[in] aP the P value of the kernel multiplied by time slot. * @return void */ - static void PickRandomPoints(Configurations &aConfigurations, + static void PickRandomPoints(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, exageostat::dataunits::Locations &aMissLocation, exageostat::dataunits::Locations &aObsLocation, const int &aP); diff --git a/inst/include/results/Results.hpp b/inst/include/results/Results.hpp index 08dd54cc..2b227d07 100644 --- a/inst/include/results/Results.hpp +++ b/inst/include/results/Results.hpp @@ -120,22 +120,16 @@ namespace exageostat::results { [[nodiscard]] double GetMMOM() const; /** - * @brief Get the Fisher matrix element 00. - * @return the Fisher matrix element 00. + * @brief Get the Fisher matrix elements. + * @return the Fisher matrix. */ - [[nodiscard]] double GetFisher00() const; + [[nodiscard]] std::vector GetFisherMatrix() const; /** - * @brief Get the Fisher matrix element 11. - * @return the Fisher matrix element 11. + * @brief Get the Predicted Missed Z matrix elements. + * @return the Z Predicted matrix. */ - [[nodiscard]] double GetFisher11() const; - - /** - * @brief Get the Fisher matrix element 22. - * @return the Fisher matrix element 22. - */ - [[nodiscard]] double GetFisher22() const; + [[nodiscard]] std::vector GetPredictedMissedValues() const; /** * @brief Set the total modeling FLOPs. @@ -240,22 +234,16 @@ namespace exageostat::results { void SetTotalFisherTime (double aTime); /** - * @brief Set the element 00 of the fisher matrix. - * @param aFisher00 Element 00 of the fisher matrix. - */ - void SetFisher00(double aFisher00); - - /** - * @brief Set the element 11 of the fisher matrix. - * @param aFisher11 element 11 of the fisher matrix. + * @brief Set the elements of the fisher matrix. + * @param aFisherMatrix Elements of the fisher matrix. */ - void SetFisher11(double aFisher11); + void SetFisherMatrix(std::vector aFisherMatrix); /** - * @brief Set the element 22 of the fisher matrix. - * @param aFisher22 element 22 of the fisher matrix. + * @brief Set the elements of the Z missed matrix. + * @param aPredictedValues Elements of the Predicted Z missed matrix. */ - void SetFisher22(double aFisher22); + void SetPredictedMissedValues(std::vector aPredictedValues); /** * @brief Print the end summary of the results. @@ -315,12 +303,10 @@ namespace exageostat::results { double mTotalModelingFlops = 0; /// Used Total Fisher Time. double mTotalFisherTime = 0; - /// Fisher matrix element 00. - double mFisher00 = 0; - /// Fisher matrix element 11. - double mFisher11 = 0; - /// Fisher matrix element 22. - double mFisher22 = 0; + /// Fisher matrix + std::vector mFisherMatrix; + /// Z miss values + std::vector mPredictedMissedValues; }; }//namespace exageostat diff --git a/inst/include/runtime/RuntimeFunctions.hpp b/inst/include/runtime/RuntimeFunctions.hpp new file mode 100644 index 00000000..db22cfb7 --- /dev/null +++ b/inst/include/runtime/RuntimeFunctions.hpp @@ -0,0 +1,230 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file RuntimeFunctions.hpp + * @brief A class for runtime static functions. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-03-10 +**/ + +#ifndef EXAGEOSTATCPP_RUNTIMEFUNCTIONS_HPP +#define EXAGEOSTATCPP_RUNTIMEFUNCTIONS_HPP + +#include +#include + +namespace exageostat::runtime { + + /** + * @class RuntimeFunctions + * @brief A class that defines runtime static functions. + * @tparam T Data Type: float or double. + * + */ + template + class RuntimeFunctions { + + public: + + /** + * @brief Computes the covariance matrix. + * @param[in] aDescriptorData pointer to the DescriptorData object holding descriptors and data. + * @param[out] apDescriptor Pointer to the descriptor for the covariance matrix. + * @param[in] aTriangularPart Specifies whether the upper or lower triangular part of the covariance matrix is stored. + * @param[in] apLocation1 Pointer to the first set of locations. + * @param[in] apLocation2 Pointer to the second set of locations. + * @param[in] apLocation3 Pointer to the third set of locations. + * @param[in] apLocalTheta Pointer to the local theta values. + * @param[in] aDistanceMetric Specifies the distance metric to use. + * @param[in] apKernel Pointer to the kernel object to use. + * @return void + * + */ + static void + CovarianceMatrix(dataunits::DescriptorData &aDescriptorData, void *apDescriptor, const int &aTriangularPart, + dataunits::Locations *apLocation1, dataunits::Locations *apLocation2, + dataunits::Locations *apLocation3, T *apLocalTheta, const int &aDistanceMetric, + const kernels::Kernel *apKernel); + + /** + * @brief Perform an asynchronous computation of MLE, MLOE, and MMOM for a tile. + * @details his function performs the computation of Maximum Likelihood Estimation (MLE), + * Maximum Likelihood on the Empirical Orthogonal Functions (MLOE), and + * Method of Moments (MMOM) for a tile asynchronously. + * @param[in] apDescExpr2 Descriptor for expression 2. + * @param[in] apDescExpr3 Descriptor for expression 3. + * @param[in] apDescExpr4 Descriptor for expression 4. + * @param[in] apDescMLOE Descriptor for MLOE. + * @param[in] apDescMMOM Descriptor for MMOM. + * @param[in] apSequence Sequence for the computation. + * @param[in] apRequest Request for the computation. + * @return void + * + */ + static void + ExaGeoStatMLETileAsyncMLOEMMOM(void *apDescExpr2, void *apDescExpr3, void *apDescExpr4, void *apDescMLOE, + void *apDescMMOM, void *apSequence, void *apRequest); + + /** + * @brief Calculate mean square prediction error (MSPE) scalar value of the prediction. + * @param[in] apDescZPredict Observed measurements. + * @param[in] apDescZMiss Missing measurements. + * @param[out] apDescError Mean Square Prediction Error (MSPE). + * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. + * @param[out] apRequest Identifies this function call (for exception handling purposes). + * @return void + * + */ + static void + ExaGeoStatMLEMSPETileAsync(void *apDescZPredict, void *apDescZMiss, void *apDescError, void *apSequence, + void *apRequest); + + /** + * @brief Copies the descriptor data to a double vector. + * @param[in] aComputation computation used in configuration. + * @param[in] aDescriptorData pointer to the DescriptorData object holding descriptors and data. + * @param[in] apDescriptor Pointer to the descriptor data. + * @param[in,out] apDoubleVector Pointer to the double vector to copy the descriptor data to. + * @return void + * + */ + static void + CopyDescriptorZ(dataunits::DescriptorData &aDescriptorData, void *apDescriptor, T *apDoubleVector); + + /** + * @brief Converts a Gaussian descriptor to a non-tiled descriptor. + * @param[in] aDescriptorData DescriptorData struct with the Gaussian descriptor. + * @param[in] apDesc Pointer to the non-tiled descriptor. + * @param[in] apTheta Theta vector. + * @return void + * + */ + static void + ExaGeoStatGaussianToNonTileAsync(dataunits::DescriptorData &aDescriptorData, void *apDesc, T *apTheta); + + /** + * @brief copy Chameleon descriptor to vector float*. + * @param[in] apDescA Exageostat descriptor A. + * @param[in] apDescB Exageostat descriptor B. + * @param[in] apDescC Exageostat descriptor C. + * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. + * @param[in] apRequest Identifies this function call (for exception handling purposes). + * @return void + * + */ + static void + ExaGeoStaStrideVectorTileAsync(void *apDescA, void *apDescB, void *apDescC, void *apSequence, void *apRequest); + + /** + * @brief Copy Chameleon descriptor to vector float*. + * @param[in] apDescA Exageostat descriptor A. + * @param[in] apDescB Exageostat descriptor B. + * @param[in] apDescC Exageostat descriptor C. + * @param[in] apDescD Exageostat descriptor D. + * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. + * @param[in] apRequest Identifies this function call (for exception handling purposes). + * @return void + * + */ + static void + ExaGeoStaStrideVectorTileAsync(void *apDescA, void *apDescB, void *apDescC, void *apDescD, void *apSequence, + void *apRequest); + + /** + * @brief Calculate determinant for triangular matrix. + * @param[in] aComputation computation used in configuration. + * @param[in] apDescA Exageostat descriptor. + * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. + * @param[in] apRequest Identifies this function call (for exception handling purposes). + * @param[in] apDescDet determinant value + * @return void + * + */ + static void + ExaGeoStatMeasureDetTileAsync(const common::Computation &aComputation, void *apDescA, void *apSequence, + void *apRequest, void *apDescDet); + + /** + * @brief Calculate determinant for triangular matrix. + * @param[in] apDescA Pointer to the descriptor of the matrix 'descA'. + * @param[in] apSequence Pointer to a sequence structure for managing asynchronous execution. + * @param[in] apRequest Pointer to a request structure for tracking the operation's status. + * @param[out] apDescNum Pointer to the descriptor of the matrix to store the sum of elements. + * @param[out] apDescTrace Pointer to the descriptor of the matrix to store the trace. + * @return void + * + */ + static void ExaGeoStatMLETraceTileAsync(void *apDescA, void *apSequence, void *apRequest, void *apDescNum, + void *apDescTrace); + + /** + * @brief Computes dot product of A.A. + * @param[in] apDescA A Descriptor + * @param[out] apDescProduct Stores the result of A.A. + * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. + * @param[in] apRequest Identifies this function call (for exception handling purposes). + * @return void + * + */ + static void ExaGeoStatDoubleDotProduct(void *apDescA, void *apDescProduct, void *apSequence, void *apRequest); + + /** + * @brief Calculate mean square error (MSE) scalar value for Bivariate kernels. + * @param[in] apDescZPre Observed measurements descZpre. + * @param[in] apDescZMiss Missing measurements descZpre + * @param[out] apDescError1 Mean Square Error (MSE) 1. + * @param[out] apDescError2 Mean Square Error (MSE) 2. + * @param[out] apDescError Mean Square Error (MSE). + * @param[in] apSequence Sequence for the computation. + * @param[in] apRequest Request for the computation. + * @return void + * + */ + static void + ExaGeoStatMLEMSPEBivariateTileAsync(void *apDescZPre, void *apDescZMiss, void *apDescError1, void *apDescError2, + void *apDescError, void *apSequence, void *apRequest); + + /** + * @brief Calculate the log likelihood of non-Gaussian MLE. + * @param[in] aComputation computation used in configuration. + * @param[in] apDescZ pointer to the Observed Measurements descriptor. + * @param[in] apDescSum The log-likelihood Sum of descriptor Z. + * @param[in] apTheta Pointer to Model parameters. + * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. + * @param[out] apRequest Identifies this function call (for exception handling purposes). + * @return void + * + */ + static void + ExaGeoStatNonGaussianLogLikeTileAsync(const common::Computation &aComputation, void *apDescZ, void *apDescSum, + const T *apTheta, void *apSequence, void *apRequest); + + /** + * @brief Transform the measurements vector inside the non-Gaussian MLE function. + * @param[in] aComputation computation used in configuration. + * @param[in] apDescZ pointer to the Observed Measurements descriptor. + * @param[in] apTheta Pointer to Model parameters. + * @param[in] apSequence Identifies the sequence of function calls that this call belongs to. + * @param[in] apRequest Identifies this function call (for exception handling purposes). + * @return void + * + */ + static void + ExaGeoStatNonGaussianTransformTileAsync(const common::Computation &aComputation, void *apDescZ, + const T *apTheta, void *apSequence, void *apRequest); + }; + + /** + * @brief Instantiates the Runtime Functions class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(RuntimeFunctions) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_RUNTIMEFUNCTIONS_HPP diff --git a/inst/include/runtime/starpu/StarPuCodeletsHeaders.hpp b/inst/include/runtime/starpu/StarPuCodeletsHeaders.hpp new file mode 100644 index 00000000..542dca14 --- /dev/null +++ b/inst/include/runtime/starpu/StarPuCodeletsHeaders.hpp @@ -0,0 +1,26 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file StarPuCodelets.hpp + * @brief Header file to include all codelet classes. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-25 +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/inst/include/runtime/starpu/concrete/dcmg-codelet.hpp b/inst/include/runtime/starpu/concrete/dcmg-codelet.hpp new file mode 100644 index 00000000..37df06f9 --- /dev/null +++ b/inst/include/runtime/starpu/concrete/dcmg-codelet.hpp @@ -0,0 +1,87 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dcmg-codelet.hpp + * @brief A class for starpu codelet dcmg. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-19 +**/ + +#ifndef EXAGEOSTATCPP_DCMG_CODELET_HPP +#define EXAGEOSTATCPP_DCMG_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class DCMG Codelet + * @brief A class for starpu codelet dcmg. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_dcmg and its CPU functions. + * + */ + template + class DCMGCodelet { + + public: + + /** + * @brief Default constructor + * + */ + DCMGCodelet() = default; + + /** + * @brief Default destructor + * + */ + ~DCMGCodelet() = default; + + /** + * @brief Inserts a task for DCMG codelet processing. + * @param[in,out] apDescriptor A pointer to the descriptor containing task information. + * @param[in] aTriangularPart An integer specifying the triangular part of the matrix (upper or lower). + * @param[in] apLocation1 A pointer to the first location object for the matrix elements. + * @param[in] apLocation2 A pointer to the second location object for the matrix elements. + * @param[in] apLocation3 A pointer to the third location object for the matrix elements. + * @param[in] apLocalTheta A pointer to the local theta value. + * @param[in] aDistanceMetric An integer specifying the distance metric to be used. + * @param[in] apKernel A pointer to the kernel function to be applied during the task execution. + * @return void + * + */ + void InsertTask(void *apDescriptor, const int &aTriangularPart, dataunits::Locations *apLocation1, + dataunits::Locations *apLocation2, dataunits::Locations *apLocation3, T *apLocalTheta, + const int &aDistanceMetric, const kernels::Kernel *apKernel); + + private: + + /** + * @brief CPU Function used by starpu_codelet struct + * @param[in] apBuffers An array of pointers to the buffers containing the matrix data. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure + * @return void + * + */ + static void cl_dcmg_function(void **apBuffers, void *apCodeletArguments); + + /// starpu_codelet struct + static struct starpu_codelet cl_dcmg; + }; + + /** + * @brief Instantiates the dcmg codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(DCMGCodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_DCMG_CODELET_HPP \ No newline at end of file diff --git a/inst/include/runtime/starpu/concrete/ddotp-codelet.hpp b/inst/include/runtime/starpu/concrete/ddotp-codelet.hpp new file mode 100644 index 00000000..4273d437 --- /dev/null +++ b/inst/include/runtime/starpu/concrete/ddotp-codelet.hpp @@ -0,0 +1,80 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ddotp-codelet.hpp + * @brief A class for starpu codelet ddotp. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_DDOTP_CODELET_HPP +#define EXAGEOSTATCPP_DDOTP_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class DDOTP Codelet + * @brief A class for starpu codelet ddotp. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_ddotp and its CPU functions. + * + */ + template + class DDOTPCodelet { + + public: + + /** + * @brief Default constructor + * + */ + DDOTPCodelet() = default; + + /** + * @brief Default destructor + * + */ + ~DDOTPCodelet() = default; + + /** + * @brief Inserts a task for DDOTP codelet processing. + * @param[in] apDescA A pointer to the descriptor for the vector. + * @param[in,out] apDescProduct A pointer to the descriptor for the dot product. + * @return void + * + */ + void InsertTask(void *apDescA, void *apDescProduct); + + private: + + /** + * @brief Executes the DDOTP codelet function for dot product calculation. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the vector size (m) and the offset (m0). + * @return void + * + */ + static void cl_ddotp_function(void **apBuffers, void *apCodeletArguments); + + /// starpu_codelet struct + static struct starpu_codelet cl_ddotp; + + }; + + /** + * @brief Instantiates the ddotp codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(DDOTPCodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_DDOTP_CODELET_HPP diff --git a/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp b/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp new file mode 100644 index 00000000..1401211c --- /dev/null +++ b/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp @@ -0,0 +1,92 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dmdet-codelet.hpp + * @brief A class for starpu codelet dmdet. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-21 +**/ + +#ifndef EXAGEOSTATCPP_DMDET_CODELET_HPP +#define EXAGEOSTATCPP_DMDET_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class DMDET Codelet + * @brief A class for starpu codelet dmdet. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_dmdet and its CPU functions. + * + */ + template + class DMDETCodelet { + + public: + + /** + * @brief Default constructor + * + */ + DMDETCodelet() = default; + + /** + * @brief Default destructor + * + */ + ~DMDETCodelet() = default; + + /** + * @brief Inserts a task for DMDET codelet processing. + * @param[in] aComputation The type of computation to be performed, such as diagonal approximation or exact dense computation. + * @param[in] apDescA A pointer to the descriptor for matrix A. + * @param[in,out] apDescDet A pointer to the descriptor for the determinant. + * @param[in] aStarPuHelpers A reference to a unique pointer of StarPuHelpers, used for accessing and managing data. + * @return void + * + */ + void InsertTask(const common::Computation& aComputation, void *apDescA, void *apDescDet, + std::unique_ptr &aStarPuHelpers); + + private: + + /** + * @brief Executes the DMDET codelet function for matrix determinant calculation. + * @param[in] apBuffers An array of pointers to the buffers containing the matrix data and the determinant. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the matrix size. + * @return void + * + */ + static void cl_dmdet_function(void **apBuffers, void *apCodeletArguments); + + /** + * @brief Calculates the determinant of a matrix. + * @param[in] apDescriptor A pointer to the matrix data. + * @param[in] aSize The size of the matrix (assumed to be square). + * @return T The calculated determinant of the matrix. + * + */ + static T core_dmdet(const T *apDescriptor,const int &aSize); + + /// starpu_codelet struct + static struct starpu_codelet cl_dmdet; + + }; + + /** + * @brief Instantiates the dmdet codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(DMDETCodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_DMDET_CODELET_HPP diff --git a/inst/include/runtime/starpu/concrete/dmloe-mmom-codelet.hpp b/inst/include/runtime/starpu/concrete/dmloe-mmom-codelet.hpp new file mode 100644 index 00000000..f9efee1b --- /dev/null +++ b/inst/include/runtime/starpu/concrete/dmloe-mmom-codelet.hpp @@ -0,0 +1,83 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dmloe-mmom-codelet.hpp + * @brief A class for starpu codelet dmloe-mmom. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-19 +**/ + +#ifndef EXAGEOSTATCPP_DMLOE_MMOM_HPP +#define EXAGEOSTATCPP_DMLOE_MMOM_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class Dmloe-Mmom Codelet + * @brief A class for starpu codelet dmloe-mmom. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_dmloe_mmom and its CPU functions. + * + */ + template + class DmloeMmomCodelet { + + public: + + /** + * @brief Default constructor + * + */ + DmloeMmomCodelet() = default; + + /** + * @brief Default destructor + * + */ + ~DmloeMmomCodelet() = default; + + /** + * @brief Inserts a task for DmloeMmom codelet processing. + * @param[in] apDescExpr1 A pointer to the descriptor for the first expression. + * @param[in] apDescExpr2 A pointer to the descriptor for the second expression. + * @param[in] apDescExpr3 A pointer to the descriptor for the third expression. + * @param[in,out] apDescMLOE A pointer to the descriptor for the MLOE result. + * @param[in,out] apDescMMOM A pointer to the descriptor for the MMOM result. + * @return void + * + */ + void InsertTask(void *apDescExpr1, void *apDescExpr2, void *apDescExpr3, void *apDescMLOE, + void *apDescMMOM); + + private: + + /** + * @brief Executes the DmloeMmom codelet function for MLOE and MMOM calculations. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the matrix dimensions and offsets. + * @return void + * + */ + static void cl_dmloe_mmom_function(void **apBuffers, void *apCodeletArguments); + + /// starpu_codelet struct + static struct starpu_codelet cl_dmloe_mmom; + }; + + /** + * @brief Instantiates the dmloe-mmom codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(DmloeMmomCodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_DMLOE_MMOM_HPP diff --git a/inst/include/runtime/starpu/concrete/dmse-bivariate-codelet.hpp b/inst/include/runtime/starpu/concrete/dmse-bivariate-codelet.hpp new file mode 100644 index 00000000..5bca487f --- /dev/null +++ b/inst/include/runtime/starpu/concrete/dmse-bivariate-codelet.hpp @@ -0,0 +1,83 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dmse-bivariate-codelet.hpp + * @brief A class for starpu codelet dmse-bivariate. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_DMSE_BIVARIATE_CODELET_HPP +#define EXAGEOSTATCPP_DMSE_BIVARIATE_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class DMSE Bivariate Codelet + * @brief A class for starpu codelet dmse-bivariate. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_dmse_bivariate and its CPU functions. + * + */ + template + class DMSEBivariateCodelet { + + public: + + /** + * @brief Default constructor + * + */ + DMSEBivariateCodelet() = default; + + /** + * @brief Default destructor + * + */ + ~DMSEBivariateCodelet() = default; + + /** + * @brief Inserts a task for DMSEBivariate codelet processing. + * @param[in] apDescZMiss A pointer to the descriptor for the observed values. + * @param[in] apDescZPre A pointer to the descriptor for the predicted values. + * @param[in,out] apDescsError A pointer to the descriptor for the total error sum. + * @param[in,out] apDescsError1 A pointer to the descriptor for the error sum for the first variable. + * @param[in,out] apDescsError2 A pointer to the descriptor for the error sum for the second variable. + * @return void + * + */ + void + InsertTask(void *apDescZMiss, void *apDescZPre, void *apDescsError, void *apDescsError1, void *apDescsError2); + + private: + + /** + * @brief Executes the DMSEBivariate codelet function for bivariate error calculation. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the vector size and offset. + * @return void + * + */ + static void cl_dmse_bivariate_function(void **apBuffers, void *apCodeletArguments); + + /// starpu_codelet struct + static struct starpu_codelet cl_dmse_bivariate; + }; + + /** + * @brief Instantiates the dmse-bivariate codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(DMSEBivariateCodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_DMSE_BIVARIATE_CODELET_HPP diff --git a/inst/include/runtime/starpu/concrete/dmse-codelet.hpp b/inst/include/runtime/starpu/concrete/dmse-codelet.hpp new file mode 100644 index 00000000..59b78d56 --- /dev/null +++ b/inst/include/runtime/starpu/concrete/dmse-codelet.hpp @@ -0,0 +1,80 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dmse-codelet.hpp + * @brief A class for starpu codelet dmse. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-21 +**/ + +#ifndef EXAGEOSTATCPP_DMSE_CODELET_HPP +#define EXAGEOSTATCPP_DMSE_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class DMSE Codelet + * @brief A class for starpu codelet dmse. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_dmse and its CPU functions. + * + */ + template + class DMSECodelet { + + public: + + /** + * @brief Constructor for DMSE codelet + * + */ + DMSECodelet() = default; + + /** + * @brief Default destructor + * + */ + ~DMSECodelet() = default; + + /** + * @brief Inserts a task for DMSE codelet processing. + * @param[in,out] apDescError A pointer to the descriptor for the error sum. + * @param[in] apDescZPredict A pointer to the descriptor for the predicted values. + * @param[in] apDescZMiss A pointer to the descriptor for the observed values. + * @return void + * + */ + void InsertTask(void *apDescError, void *apDescZPredict, void *apDescZMiss); + + private: + + /** + * @brief Executes the DMSE codelet function for error calculation. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the vector size and offset. + * @retur void + * + */ + static void cl_dmse_function(void **apBuffers, void *apCodeletArguments); + + /// starpu_codelet struct + static struct starpu_codelet cl_dmse; + }; + + /** + * @brief Instantiates the dmse codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(DMSECodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_DMSE_CODELET_HPP diff --git a/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp b/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp new file mode 100644 index 00000000..2cc0ecc1 --- /dev/null +++ b/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp @@ -0,0 +1,91 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dtrace-codelet.hpp + * @brief A class for starpu codelet dtrace. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_DTRACE_CODELET_HPP +#define EXAGEOSTATCPP_DTRACE_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class DTRACE Codelet + * @brief A class for starpu codelet dtrace. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_dtrace and its CPU functions. + * + */ + template + class DTRACECodelet { + + public: + + /** + * @brief Default constructor + * + */ + DTRACECodelet() = default; + + /** + * @brief Default destructor + * + */ + ~DTRACECodelet() = default; + + /** + * @brief Inserts a task for DTRACE codelet processing. + * @param[in] apDescA A pointer to the descriptor for the matrix. + * @param[in,out] apDescNum A pointer to the descriptor for the sum. + * @param[in,out] apDescTrace A pointer to the descriptor for the trace. + * @return void + * + */ + void InsertTask(void *apDescA, void *apDescNum, void *apDescTrace); + + private: + + /** + * @brief Executes the DTRACE codelet function for matrix trace calculation. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the matrix size. + * @return void + * + */ + static void cl_dtrace_function(void **apBuffers, void *apCodeletArguments); + + /** + * @brief Calculates the trace of a matrix. + * @param[in] pDescriptor A pointer to the matrix data. + * @param[in] aSize The size of the matrix (assumed to be square). + * @param[in,out] pTrace A pointer to the buffer where the trace value will be stored. + * @return The calculated trace of the matrix. + * + */ + static double core_dtrace(const T *pDescriptor,const int &aSize, T *pTrace); + + /// starpu_codelet struct + static struct starpu_codelet cl_dtrace; + + }; + + /** + * @brief Instantiates the dtrace codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(DTRACECodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_DTRACE_CODELET_HPP diff --git a/inst/include/runtime/starpu/concrete/dzcpy-codelet.hpp b/inst/include/runtime/starpu/concrete/dzcpy-codelet.hpp new file mode 100644 index 00000000..45459a61 --- /dev/null +++ b/inst/include/runtime/starpu/concrete/dzcpy-codelet.hpp @@ -0,0 +1,82 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dzcpy-codelet.hpp + * @brief A class for starpu codelet dzcpy. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_DZCPY_CODELET_HPP +#define EXAGEOSTATCPP_DZCPY_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class DZCPY Codelet + * @brief A class for starpu codelet dzcpy. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_dzcpy and its CPU functions. + * + */ + template + class DZCPYCodelet { + + public: + + /** + * @brief Default constructor + * + */ + DZCPYCodelet() = default; + + /** + * @brief Default destructor + * + */ + ~DZCPYCodelet() = default; + + /** + * @brief Inserts a task for DZCPY codelet processing. + * @param[in,out] apDescriptor A pointer to the descriptor for the vector. + * @param[in] apDoubleVector A pointer to the double vector to be copied. + * @return void + * + */ + void InsertTask(void *apDescriptor, void *apDoubleVector); + + private: + + /** + * @brief Executes the DZCPY codelet function for copying a double vector. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the vector size, + * offset, and the pointer to the destination vector. + * @return void + * + */ + static void cl_dzcpy_function(void **apBuffers, void *apCodeletArguments); + + /// starpu_codelet struct + static struct starpu_codelet cl_dzcpy; + }; + + /** + * @brief Instantiates the dzcpy codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(DZCPYCodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_DZCPY_CODELET_HPP + + diff --git a/inst/include/runtime/starpu/concrete/gaussian-to-non-codelet.hpp b/inst/include/runtime/starpu/concrete/gaussian-to-non-codelet.hpp new file mode 100644 index 00000000..ad3ac512 --- /dev/null +++ b/inst/include/runtime/starpu/concrete/gaussian-to-non-codelet.hpp @@ -0,0 +1,92 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file gaussian-to-non-codelet.hpp + * @brief A class for starpu codelet gaussian-to-non. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_GAUSSIAN_TO_NON_CODELET_H +#define EXAGEOSTATCPP_GAUSSIAN_TO_NON_CODELET_H + +#include + +namespace exageostat::runtime { + + /** + * @class Gaussian-To-Non Codelet + * @brief A class for starpu codelet gaussian-to-non. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_gaussian_to_non and its CPU functions. + * + */ + template + class GaussianCodelet { + + public: + + /** + * @brief Default constructor + * + */ + GaussianCodelet() = default; + + /** + * @brief Default destructor + * + */ + ~GaussianCodelet() = default; + + /** + * @brief Inserts a task for Gaussian to non-Gaussian conversion codelet processing. + * @param[in,out] apDesc A pointer to the descriptor for the matrix tile. + * @param[in] apTheta A pointer to the transformation parameters. + * @return void + * + */ + void InsertTask(void *apDesc, T *apTheta); + + private: + + /** + * @brief Executes the Gaussian to non-Gaussian conversion codelet function. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the matrix size, + * offset, and the transformation parameters. + * @return void + * + */ + static void cl_gaussian_to_non_function(void **apBuffers, void *apCodeletArguments); + + /** + * @brief Transforms data from a Gaussian distribution to a non-Gaussian distribution. + * @param[in,out] apDescriptorZ A pointer to the array of data to be transformed. This array is modified in place. + * @param[in] apLocalTheta A pointer to the array of transformation parameters. The first element is the mean (`xi`), + * the second element is the scale (`omega`), the third element is the skewness (`g`), and the fourth element is the kurtosis (`h`). + * @param[in] aSize The size of the data array (`pZ`) and the transformation parameters array (`apLocalTheta`). + * @throws std::runtime_error If the kurtosis parameter (`h`) is negative, indicating an invalid transformation parameter. + * @return void + * + */ + static void core_gaussian_to_non(T *apDescriptorZ, const T *apLocalTheta, const int &aSize); + + /// starpu_codelet struct + static struct starpu_codelet cl_gaussian_to_non; + }; + + /** + * @brief Instantiates the gaussian-to-non codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(GaussianCodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_GAUSSIAN_TO_NON_CODELET_H diff --git a/inst/include/runtime/starpu/concrete/non-gaussian-loglike-codelet.hpp b/inst/include/runtime/starpu/concrete/non-gaussian-loglike-codelet.hpp new file mode 100644 index 00000000..ea5cfe34 --- /dev/null +++ b/inst/include/runtime/starpu/concrete/non-gaussian-loglike-codelet.hpp @@ -0,0 +1,93 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file non-gaussian-loglike-codelet.hpp + * @brief A class for starpu codelet non-gaussian-loglike. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-26 +**/ + +#ifndef EXAGEOSTATCPP_NON_GAUSSIAN_LOGLIKE_CODELET_HPP +#define EXAGEOSTATCPP_NON_GAUSSIAN_LOGLIKE_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class NonGaussianLoglike + * @brief A class for starpu codelet non gaussian loglike. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_non_gaussian_loglike and its CPU functions. + * + */ + template + class NonGaussianLoglike { + + public: + + /** + * @brief Constructor for NonGaussianLoglike + * + */ + NonGaussianLoglike() = default; + + /** + * @brief Default destructor + * + */ + ~NonGaussianLoglike() = default; + + /** + * @brief Inserts a task for Non-Gaussian log-likelihood codelet processing. + * @param[in] apDescZ A pointer to the descriptor for the dataset. + * @param[in,out] apDescSum A pointer to the descriptor for the sum. + * @param[in] apTheta A pointer to the transformation parameters. + * @param[in] aStarPuHelpers A reference to a unique pointer of StarPuHelpers, used for accessing and managing data. + * @return void + * + */ + void + InsertTask(void *apDescZ, void *apDescSum, const T *apTheta, std::unique_ptr &aStarPuHelper); + + private: + + /** + * @brief Executes the Non-Gaussian log-likelihood codelet function. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the dataset size, + * offset, and the transformation parameters. + * @return void + * + */ + static void cl_non_gaussian_loglike_function(void **apBuffers, void *apCodeletArguments); + + /** + * @brief Helper function for calculating the log-likelihood under a non-Gaussian distribution. + * @param[in] apDescriptorZ A pointer to the dataset. + * @param[in] apLocalTheta A pointer to the transformation parameters. + * @param[in] aSize The size of the dataset. + * @return T The calculated log-likelihood of the dataset under a non-Gaussian distribution. + * + */ + static double core_non_gaussian_loglike_helper(const T *apDescriptorZ, const T *apLocalTheta, const int &aSize); + + /// starpu_codelet struct + static struct starpu_codelet cl_non_gaussian_loglike; + }; + + /** + * @brief Instantiates the non-gaussian-loglike class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(NonGaussianLoglike) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_NON_GAUSSIAN_LOGLIKE_CODELET_HPP diff --git a/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp b/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp new file mode 100644 index 00000000..86cc001f --- /dev/null +++ b/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp @@ -0,0 +1,130 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file non-gaussian-transform-codelet.hpp + * @brief A class for starpu codelet non-gaussian-transform. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-26 +**/ + +#ifndef EXAGEOSTATCPP_NON_GAUSSIAN_TRANSFORM_CODELET_HPP +#define EXAGEOSTATCPP_NON_GAUSSIAN_TRANSFORM_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class NonGaussianTransform Codelet + * @brief A class for starpu codelet non gaussian transform. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_non_gaussian_transform and its CPU functions. + * + */ + template + class NonGaussianTransform { + + public: + + /** + * @brief Default constructor + * + */ + NonGaussianTransform() = default; + + /** + * @brief Default destructor + * + */ + ~NonGaussianTransform() = default; + + /** + * @brief Inserts a task for Non-Gaussian transformation codelet processing. + * @param[in,out] apDescZ A pointer to the descriptor for the dataset. + * @param[in] apTheta A pointer to the transformation parameters. + * @param[in] apStarPuHelpers A reference to a unique pointer of StarPuHelpers, used for accessing and managing data. + * @return void + * + */ + void InsertTask(void *apDescZ, const T *apTheta, std::unique_ptr &apStarPuHelpers); + + private: + + /** + * @brief Executes the Non-Gaussian transformation codelet function. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the dataset size, + * offset, and the transformation parameters. + * @return void + * + */ + static void cl_non_gaussian_transform_function(void **apBuffers, void *apCodeletArguments); + + /** + * @brief Helper function for transforming a dataset to a non-Gaussian representation. It applies + * the Non-Gaussian transformation to each element of a dataset using the + * Newton-Raphson method for finding the root of a function. + * @param[in,out] apDescripZ A pointer to the dataset to be transformed. + * @param[in] apLocalTheta A pointer to the transformation parameters. + * @param[in] aSize The size of the dataset. + * @return void + * + */ + static void core_non_gaussian_transform_helper(T *apDescripZ, const T *apLocalTheta, const int &aSize); + + /** + * @brief Implements the Newton-Raphson method for finding the root of a function. + * @param[in] apDescriptorZ The initial guess for the root. + * @param[in] aTransLocation The location parameter of the transformation. + * @param[in] aTransScale The scale parameter of the transformation. + * @param[in] aTransShape The shape parameter of the transformation. + * @param[in] aTransKurtosis The kurtosis parameter of the transformation. + * @param[in] aEpsilon The error threshold for the root-finding process. + * @return T The calculated root of the function. + * + */ + static double newton_raphson(T apDescriptorZ, T aTransLocation, T aTransScale, T aTransShape, T aTransKurtosis, T aEpsilon); + + /** + * @brief Calculates the Non-Gaussian transformation of a value. + * @param[in] aOriginalValue The original value to be transformed. + * @param[in] aCurrentValue The current value of the transformation variable. + * @param[in] aTransLocation The location parameter of the transformation. + * @param[in] aTransScale The scale parameter of the transformation. + * @param[in] aTransShape The shape parameter of the transformation. + * @param[in] aTransKurtosis The kurtosis parameter of the transformation. + * @return T The transformed value. + * + */ + static double tukeyGHTransfor(T aOriginalValue, T aCurrentValue, T aTransLocation, T aTransScale, T aTransShape, T aTransKurtosis); + + /** + * @brief Calculates the derivative of the Non-Gaussian transformation. + * @param[in] aCurrentValue The current value of the transformation variable. + * @param[in] aTransScale The scale parameter of the transformation. + * @param[in] aTransShape The shape parameter of the transformation. + * @param[in] aTransKurtosis The kurtosis parameter of the transformation. + * @return T The derivative of the transformation at the given value. + * + */ + static double tukeyGHDiferencial(T aCurrentValue, T aTransScale, T aTransShape, T aTransKurtosis); + + /// starpu_codelet struct + static struct starpu_codelet cl_non_gaussian_transform; + }; + + /** + * @brief Instantiates the non-gaussian-transform codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(NonGaussianTransform) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_NON_GAUSSIAN_TRANSFORM_CODELET_HPP diff --git a/inst/include/runtime/starpu/concrete/stride-vec-codelet.hpp b/inst/include/runtime/starpu/concrete/stride-vec-codelet.hpp new file mode 100644 index 00000000..90d76a59 --- /dev/null +++ b/inst/include/runtime/starpu/concrete/stride-vec-codelet.hpp @@ -0,0 +1,81 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file stride-vec-codelet.hpp + * @brief A class for starpu codelet stride-vec. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_STRIDE_VEC_CODELET_HPP +#define EXAGEOSTATCPP_STRIDE_VEC_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class stride-vec Codelet + * @brief A class for starpu codelet stride-vec. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_stride_vec and its CPU functions. + * + */ + template + class STRIDEVECCodelet { + + public: + + /** + * @brief Default constructor + * + */ + STRIDEVECCodelet() = default; + + /** + * @brief Default destructor + * + */ + ~STRIDEVECCodelet() = default; + + /** + * @brief Inserts a task for STRIDE vector operation codelet processing. + * @param[in] apDescA A pointer to the descriptor for the source vector. + * @param[in,out] apDescB A pointer to the descriptor for the first destination vector. + * @param[in,out] apDescC A pointer to the descriptor for the second destination vector. + * @return void + * + */ + void InsertTask(const void *apDescA, void *apDescB, void *apDescC); + + private: + + /** + * @brief Executes the STRIDE vector operation codelet function. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the vector size, + * offset, and the stride factor. + * @return void + * + */ + static void cl_stride_vec_function(void **apBuffers, void *apCodeletArguments); + + /// starpu_codelet struct + static struct starpu_codelet cl_stride_vec; + }; + + /** + * @brief Instantiates the stride-vec codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(STRIDEVECCodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_STRIDE_VEC_CODELET_HPP diff --git a/inst/include/runtime/starpu/concrete/tri-stride-vec-codelet.hpp b/inst/include/runtime/starpu/concrete/tri-stride-vec-codelet.hpp new file mode 100644 index 00000000..6fc939e6 --- /dev/null +++ b/inst/include/runtime/starpu/concrete/tri-stride-vec-codelet.hpp @@ -0,0 +1,82 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file tri-stride-vec-codelet.hpp + * @brief A class for starpu codelet tri-stride-vec. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_TRI_STRIDE_VEC_CODELET_HPP +#define EXAGEOSTATCPP_TRI_STRIDE_VEC_CODELET_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class TriStrideVecCodelet Codelet + * @brief A class for starpu codelet tri_stride_vec. + * @tparam T Data Type: float or double + * @details This class encapsulates the struct cl_tri_stride_vec and its CPU functions. + * + */ + template + class TriStrideVecCodelet { + + public: + + /** + * @brief Default constructor + * + */ + TriStrideVecCodelet() = default; + + /** + * @brief Default destructor + * + */ + ~TriStrideVecCodelet() = default; + + /** + * @brief Inserts a task for TriStride vector operation codelet processing. + * @param[in] apDescA A pointer to the descriptor for the source vector. + * @param[in,out] apDescB A pointer to the descriptor for the first destination vector. + * @param[in,out] apDescC A pointer to the descriptor for the second destination vector. + * @param[in,out] apDescD A pointer to the descriptor for the third destination vector. + * @return void + * + */ + void InsertTask(const void *apDescA, void *apDescB, void *apDescC, void *apDescD); + + private: + + /** + * @brief Executes the TriStride vector operation codelet function. + * @param[in] apBuffers An array of pointers to the buffers. + * @param[in] apCodeletArguments A pointer to the codelet arguments structure, which includes the vector size, + * offset, and the stride factor. + * @return void + * + */ + static void cl_tri_stride_vec_function(void **apBuffers, void *apCodeletArguments); + + /// starpu_codelet struct + static struct starpu_codelet cl_tri_stride_vec; + }; + + /** + * @brief Instantiates the tri-stride-vec codelet class for float and double types. + * @tparam T Data Type: float or double + * + */ + EXAGEOSTAT_INSTANTIATE_CLASS(TriStrideVecCodelet) + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_TRI_STRIDE_VEC_CODELET_HPP diff --git a/inst/include/runtime/starpu/helpers/StarPuHelpers.hpp b/inst/include/runtime/starpu/helpers/StarPuHelpers.hpp new file mode 100644 index 00000000..fa2ed425 --- /dev/null +++ b/inst/include/runtime/starpu/helpers/StarPuHelpers.hpp @@ -0,0 +1,114 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file StarPuHelpers.hpp + * @brief An interface for StarPu helpers. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_STARPUHELPERS_HPP +#define EXAGEOSTATCPP_STARPUHELPERS_HPP + +#include + +#include +#include + +namespace exageostat::runtime { + + /** + * @class StarPuHelpers + * @brief A class that defines the interface for StarPu helpers. + * @tparam T Data Type: float or double. + * + */ + class StarPuHelpers { + public: + + /** + * @brief Initialize the runtime option structure for either HiCMA or CHAMELEON. + * @param[in, out] apOptions The options structure that needs to be initialized. + * @param[in] apSequence The sequence structure to associate in the options. + * @param[in] apRequest The request structure to associate in the options. + * @return void + * + */ + virtual void + ExaGeoStatOptionsInit(void *apOptions, void *apSequence, void *apRequest) = 0; + + /** + * @brief Submit the release of the workspaces associated to the options structure. + * @param[in,out] apOptions The options structure for which to workspaces will be released + * @return void + * + */ + virtual void ExaGeoStatOptionsFree(void *apOptions) = 0; + + /** + * @brief Finalize the runtime option structure for either HiCMA or CHAMELEON. + * @param[in,out] apOptions The options structure that needs to be finalized. + * @return void + * + */ + virtual void ExaGeoStatOptionsFinalize(void *apOptions) = 0; + + /** + * @brief Get the pointer to the data or the runtime handler associated to the piece of data (m, n) in desc. + * @param[in] apDescriptor The descriptor to which belongs the piece of data + * @param[in] aDescRow The row coordinate of the piece of data in the matrix + * @param[in] aDescCol The column coordinate of the piece of data in the matrix + * @return void + * + */ + virtual void *ExaGeoStatDataGetAddr(void *apDescriptor, const int &aDescRow, const int &aDescCol) = 0; + + /** + * @brief Get the number of tile rows of the sub-matrix + * @param[in] apDescriptor + * @return int + * + */ + virtual int GetMT(void *apDescriptor) = 0; + + /** + * @brief Get the descriptor number of rows + * @param[in] apDescriptor + * @return int + * + */ + virtual int GetM(void *apDescriptor) = 0; + + /** + * @brief Get the descriptor number of rows in a tile + * @param[in] apDescriptor + * @return int + * + */ + virtual int GetMB(void *apDescriptor) = 0; + + /** + * @brief Get the descriptor options + * @return void pointer to descriptor_option + * @return void + * + */ + virtual void *GetOptions() = 0; + + /** + * @brief Delete the options object + * @param apOptions + * @return void + * + */ + virtual void DeleteOptions(void *apOptions) = 0; + + }; + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_STARPUHELPERS_HPP diff --git a/inst/include/runtime/starpu/helpers/StarPuHelpersFactory.hpp b/inst/include/runtime/starpu/helpers/StarPuHelpersFactory.hpp new file mode 100644 index 00000000..656e02a4 --- /dev/null +++ b/inst/include/runtime/starpu/helpers/StarPuHelpersFactory.hpp @@ -0,0 +1,42 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file StarPuHelpersFactory.hpp + * @brief Factory for StarPu helpers. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_STARPUHELPERSFACTORY_HPP +#define EXAGEOSTATCPP_STARPUHELPERSFACTORY_HPP + +#include + +namespace exageostat::runtime { + + /** + * @class StarPuHelpersFactory + * @brief A class that creates StarPu helpers based on the input computation type. + * + */ + class StarPuHelpersFactory { + + public: + + /** + * @brief Creates a StarPu helper. + * @param[in] aComputation The computation type to create the solver for. + * @return Unique pointer to the created StarPu helper. + * + */ + static std::unique_ptr CreateStarPuHelper(const common::Computation &aComputation); + + }; + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_STARPUHELPERSFACTORY_HPP diff --git a/inst/include/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.hpp b/inst/include/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.hpp new file mode 100644 index 00000000..bbc1ae21 --- /dev/null +++ b/inst/include/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.hpp @@ -0,0 +1,108 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ChameleonStarPuHelpers.hpp + * @brief A class for Chameleon implementation of StarPu helpers interface StarPuHelpers.hpp. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_CHAMELEONSTARPUHELPERS_HPP +#define EXAGEOSTATCPP_CHAMELEONSTARPUHELPERS_HPP + +#include + +namespace exageostat::runtime { + + /** + * @brief ChameleonStarPuHelpers is a concrete implementation of StarPuHelpers interface for Chameleon library. + * + */ + class ChameleonStarPuHelpers : public StarPuHelpers { + public: + + /** + * @brief Default constructor. + */ + ChameleonStarPuHelpers() = default; + + /** + * @brief Default destructor. + */ + ~ChameleonStarPuHelpers() = default; + + /** + * @brief Initialize the runtime option structure for CHAMELEON + * @copydoc StarPuHelpers::ExaGeoStatOptionsInit() + * + */ + void + ExaGeoStatOptionsInit(void *apOptions, void *apSequence, void *apRequest) override; + + /** + * @brief Submit the release of the workspaces associated to the options structure. + * @copydoc StarPuHelpers::ExaGeoStatOptionsFree() + * + */ + void + ExaGeoStatOptionsFree(void *apOptions) override; + + /** + * @brief Finalize the runtime option structure for CHAMELEON. + * @copydoc StarPuHelpers::ExaGeoStatOptionsFinalize() + * + */ + void + ExaGeoStatOptionsFinalize(void *apOptions) override; + + /** + * @brief Get the pointer to the data or the runtime handler associated to the piece of data (m, n) in desc. + * @copydoc StarPuHelpers::ExaGeoStatDataGetAddr() + * + */ + void *ExaGeoStatDataGetAddr(void *apDescriptor, const int &aDescRow, const int &aDescCol) override; + + /** + * @brief Get the number of tile rows of the sub-matrix + * @copydoc StarPuHelpers::GetMT() + * + */ + int GetMT(void *apDescriptor) override; + + /** + * @brief Get the descriptor number of rows + * @copydoc StarPuHelpers::GetM() + * + */ + int GetM(void *apDescriptor) override; + + /** + * @brief Get the descriptor number of rows in a tile + * @copydoc StarPuHelpers::GetMB() + * + */ + int GetMB(void *apDescriptor) override; + + /** + * @brief Get the descriptor options + * @copydoc StarPuHelpers::GetOptions() + * + */ + void *GetOptions() override; + + /** + * @brief Delete the options object + * @copydoc StarPuHelpers::DeleteOptions() + * + */ + void DeleteOptions(void *apOptions) override; + + }; + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_CHAMELEONSTARPUHELPERS_HPP diff --git a/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp b/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp new file mode 100644 index 00000000..a5fd9cc0 --- /dev/null +++ b/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp @@ -0,0 +1,107 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file HicmaStarPuHelpers.hpp + * @brief A class for Hicma implementation of StarPu helpers interface StarPuHelpers.hpp. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-25 +**/ + +#ifndef EXAGEOSTATCPP_HICMASTARPUHELPERS_HPP +#define EXAGEOSTATCPP_HICMASTARPUHELPERS_HPP + +#include + +namespace exageostat::runtime { + + /** + * @brief HicmaStarPuHelpers is a concrete implementation of StarPuHelpers interface for Hicma library. + * + */ + class HicmaStarPuHelpers : public StarPuHelpers { + public: + + /** + * @brief Default constructor. + * + */ + HicmaStarPuHelpers() = default; + + /** + * @brief Default destructor. + * + */ + ~HicmaStarPuHelpers() = default; + + /** + * @brief Initialize the runtime option structure for HiCMA + * @copydoc StarPuHelpers::ExaGeoStatOptionsInit() + * + */ + void ExaGeoStatOptionsInit(void *apOptions, void *apSequence, void *apRequest) override; + + /** + * @brief Submit the release of the workspaces associated to the options structure. + * @copydoc StarPuHelpers::ExaGeoStatOptionsFree() + * + */ + void ExaGeoStatOptionsFree(void *apOptions) override; + + /** + * @brief Finalize the runtime option structure for HiCMA. + * @copydoc StarPuHelpers::ExaGeoStatOptionsFinalize() + * + */ + void ExaGeoStatOptionsFinalize(void *apOptions) override; + + /** + * @brief Get the pointer to the data or the runtime handler associated to the piece of data (m, n) in desc. + * @copydoc StarPuHelpers::ExaGeoStatDataGetAddr() + * + */ + void *ExaGeoStatDataGetAddr(void *apDescriptor, const int& aDescRow, const int& aDescCol) override; + + /** + * @brief Get the number of tile rows of the sub-matrix + * @copydoc StarPuHelpers::GetMT() + * + **/ + int GetMT(void *apDescriptor) override; + + /** + * @brief Get the descriptor number of rows + * @copydoc StarPuHelpers::GetM() + * + */ + int GetM(void *apDescriptor) override; + + /** + * @brief Get the descriptor number of rows in a tile + * @copydoc StarPuHelpers::GetMB() + * + */ + int GetMB(void *apDescriptor) override; + + /** + * @brief Get the descriptor options + * @copydoc StarPuHelpers::GetOptions) + * + */ + void *GetOptions() override; + + /** + * @brief Delete the options object + * @copydoc StarPuHelpers::DeleteOptions() + * + */ + void DeleteOptions(void *apOptions) override; + + }; + +}//namespace exageostat + +#endif //EXAGEOSTATCPP_HICMASTARPUHELPERS_HPP diff --git a/inst/include/utilities/ErrorHandler.hpp b/inst/include/utilities/ErrorHandler.hpp index 4cb4214b..f9cb1c84 100644 --- a/inst/include/utilities/ErrorHandler.hpp +++ b/inst/include/utilities/ErrorHandler.hpp @@ -15,8 +15,6 @@ #ifndef EXAGEOSTATCPP_ERRORHANDLER_HPP #define EXAGEOSTATCPP_ERRORHANDLER_HPP -#include - #ifdef USE_CUDA #include #endif diff --git a/inst/include/utilities/Logger.hpp b/inst/include/utilities/Logger.hpp index ae9aacb3..c0e2b5db 100644 --- a/inst/include/utilities/Logger.hpp +++ b/inst/include/utilities/Logger.hpp @@ -19,16 +19,14 @@ #include #include #include -#include #include #include #include -#include /** * @def DEFAULT_PRECISION - * @brief The value of the default C++ std::cout number of precision. + * @brief The value of the default C++ std::std::cout number of precision. */ #define DEFAULT_PRECISION 6 @@ -37,11 +35,11 @@ * @brief Verbose macro for logging and debugging mode. */ #define VERBOSE(msg) \ - if(Configurations::GetVerbosity() == exageostat::common::Verbose::DETAILED_MODE && \ + if(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::DETAILED_MODE && \ !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) { \ std::ostringstream oss; \ oss << "\t\t\t " << msg << std::endl; \ - EXAGEOSTAT_PRINTER(oss.str()); \ + std::cout << oss.str(); \ } /** @@ -49,10 +47,10 @@ * @brief LOGGER_1 macro for logging outputs with double taps and new line at the end. */ #define LOGGER_1(msg) \ - if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ std::ostringstream oss; \ oss << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg << std::endl; \ - EXAGEOSTAT_PRINTER(oss.str()); \ + std::cout << oss.str(); \ } /** @@ -60,10 +58,10 @@ * @brief LOGGER_2 macro for logging outputs with double taps and without new line at the end. */ #define LOGGER_2(msg, A) \ - if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ std::ostringstream oss; \ oss << "\t\t " << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; \ - EXAGEOSTAT_PRINTER(oss.str()); \ + std::cout << oss.str(); \ } /** * @def LOGGER_CONTROL(x, A, B, FUNC, ...) @@ -85,10 +83,10 @@ * @brief LOGGER_PRECISION_1 macro for logging outputs without any taps, without new line at the end, and with customized precision. */ #define LOGGER_PRECISION_1(msg, precision) \ - if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ + if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()){ \ std::ostringstream oss; \ oss << std::fixed << std::setprecision(precision) << msg; \ - EXAGEOSTAT_PRINTER(oss.str()); \ + std::cout << oss.str(); \ } /** @@ -96,10 +94,10 @@ * @brief LOGGER_PRECISION_2 macro for logging outputs without any taps, without new line at the end, and with default C++ precision. */ #define LOGGER_PRECISION_2(msg) \ - if(!(Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) {\ + if(!(exageostat::configurations::Configurations::GetVerbosity() == exageostat::common::Verbose::QUIET_MODE) && !exageostat::helpers::CommunicatorMPI::GetInstance()->GetRank()) {\ std::ostringstream oss; \ oss << std::fixed << std::setprecision(DEFAULT_PRECISION) << msg; \ - EXAGEOSTAT_PRINTER(oss.str()); \ + std::cout << oss.str(); \ } /** * @def LOGGER_PRECISION_CONTROL diff --git a/inst/include/utilities/Printer.hpp b/inst/include/utilities/Printer.hpp deleted file mode 100644 index 1185e936..00000000 --- a/inst/include/utilities/Printer.hpp +++ /dev/null @@ -1,38 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file Printer.hpp - * @version 1.1.0 - * @brief Provides printing functionality for output messages. - * @details Defines macros for printing messages to the console or R output. - * @author Mahmoud ElKarargy - * @author David Helmy - * @date 2024-01-20 -**/ - -#ifndef EXAGEOSTATCPP_PRINTER_HPP -#define EXAGEOSTATCPP_PRINTER_HPP - -#include -#ifdef USING_R -//#include -#endif - -/** - * @def EXAGEOSTAT_PRINTER(message) - * @brief Macro for printing messages. - * @details Depending on whether the code is compiled with Rcpp support, it prints the message to the console or R output. - */ -#ifdef USING_R -#define EXAGEOSTAT_PRINTER(message) \ - std::cout <<(message); -// Rcpp::Rcout<<(message); -#else -#define EXAGEOSTAT_PRINTER(message) \ - std::cout<<(message); -#endif - -#endif //EXAGEOSTATCPP_PRINTER_HPP diff --git a/man/Data.Rd b/man/Data.Rd new file mode 100644 index 00000000..dd9552d0 --- /dev/null +++ b/man/Data.Rd @@ -0,0 +1,40 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file Data.Rd +% @brief roxygen2 documentation for the R wrapper of ExaGeoStatData class +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{ExaGeoStatData} +\alias{ExaGeoStatData} +\title{ExaGeoStatData Class} + +\description{ +The ExaGeoStatData class represents a data component in the ExaGeoStat system, that manages geo-statistical data with functions +for location and descriptor manipulation. It is initialized with the size and dimension of the data. +} + +\section{Constructor}{ + \code{\link{ExaGeoStatData}} Creates a new instance of the \code{ExaGeoStatData} class. + \code{ExaGeoStatData(size, dimension)} + \describe{ + \item{\code{size}}{An integer representing the size of the data.} + \item{\code{dimension}}{A string representing the dimensions of the data.} +} +} + +\value{ +An object of class \code{ExaGeoStatData} representing a data component with the specified size and dimension. +} + +\examples{ +problem_size <- 4 +dimension = "2D" +empty_data <- new(Data, problem_size, dimension) +} + +\keyword{S4 class} diff --git a/man/Hardware.Rd b/man/Hardware.Rd new file mode 100644 index 00000000..d59f956b --- /dev/null +++ b/man/Hardware.Rd @@ -0,0 +1,50 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file Hardware.Rd +% @brief roxygen2 documentation for the R wrapper of ExaGeoStatHardware class. +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{ExaGeoStatHardware} +\alias{ExaGeoStatHardware} +\title{ExaGeoStatHardware Class} + +\description{ +The ExaGeoStatHardware class represents a hardware component in the ExaGeoStat system. +It is initialized with computation mode, and two integers representing number of CPU cores and number of GPU cores. +} + +\section{Constructor}{ + \code{\link{ExaGeoStatHardware}} Creates a new instance of the \code{ExaGeoStatHardware} class. + \code{ExaGeoStatHardware(computation, num_of_cpus, num_of_gpus)} + \describe{ + \item{\code{computation}}{A string representing the computation mode.} + \item{\code{num_of_cpus}}{An integer representing number of CPU cores.} + \item{\code{num_of_gpus}}{An integer representing number of GPU cores.} +} +} + +\section{Methods}{ +\subsection{finalize_hardware}{ +\code{finalize_hardware()} Manually finalizes the hardware by resetting the context. +} +} + +\value{ +An object of class \code{ExaGeoStatHardware} representing a hardware component with the specified component and number of CPU cores and GPU cores. +} + +\examples{ +ncores <- 2 +ngpus <- 0 +computation <- "exact" +hardware <- new(Hardware, computation, ncores, ngpus) + +hardware$finalize_hardware() +} + +\keyword{S4 class} diff --git a/man/fisher.Rd b/man/fisher.Rd new file mode 100644 index 00000000..69ddb2de --- /dev/null +++ b/man/fisher.Rd @@ -0,0 +1,93 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file fisher.Rd +% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatFisher function. +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{fisher} +\alias{fisher} +\title{Compute the Fisher information matrix for a given data and theta vector} + +\usage{fisher(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", z_miss, data, +matrix = R_NilValue, x = R_NilValue, y = R_NilValue, z = R_NilValue)} + +\arguments{ +\item{kernel}{A string specifying the kernel to use - available kernels +( "BivariateMaternFlexible", + "BivariateMaternParsimonious", + "BivariateSpacetimeMaternStationary", + "TrivariateMaternParsimonious", + "UnivariateExpNonGaussian", + "UnivariateMaternDbeta", + "UnivariateMaternDdbetaBeta", + "UnivariateMaternDdbetaNu", + "UnivariateMaternDdnuNu", + "UnivariateMaternDdsigmaSquare", + "UnivariateMaternDdsigmaSquareBeta", + "UnivariateMaternDdsigmaSquareNu", + "UnivariateMaternDnu", + "UnivariateMaternDsigmaSquare", + "UnivariateMaternNonGaussian", + "UnivariateMaternNuggetsStationary", + "UnivariateMaternStationary", + "UnivariatePowExpStationary", + "UnivariateSpacetimeMaternStationary", + "bivariate_matern_flexible", + "bivariate_matern_parsimonious", + "bivariate_spacetime_matern_stationary", + "trivariate_matern_parsimonious", + "univariate_exp_non_gaussian", + "univariate_matern_dbeta", + "univariate_matern_ddbeta_beta", + "univariate_matern_ddbeta_nu", + "univariate_matern_ddnu_nu", + "univariate_matern_ddsigma_square", + "univariate_matern_ddsigma_square_beta", + "univariate_matern_ddsigma_square_nu", + "univariate_matern_dnu", + "univariate_matern_dsigma_square", + "univariate_matern_non_gaussian", + "univariate_matern_nuggets_stationary", + "univariate_matern_stationary", + "univariate_pow_exp_stationary", + "univariate_spacetime_matern_stationary" +)} +\item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} +\item{estimated_theta}{A list of estimated theta parameters.} +\item{dts}{A numeric value representing the time step size.} +\item{lts}{A numeric value representing the length step size. Default is 0.} +\item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D".} +\item{z_miss}{A numeric value representing the missing value indicator.} +\item{data}{A list of data vectors.} +\item{matrix}{A matrix object. Default is `R_NilValue`.} +\item{x}{ A numeric vector. Default is `R_NilValue`.} +\item{y}{ A numeric vector. Default is `R_NilValue`.} +\item{z}{ A numeric vector. Default is `R_NilValue`.} +} + +\value{A vector containing the Fisher information matrix elements.} + +\description{This function computes the Fisher information matrix for a given dataset and theta vector, +using a specified kernel and distance metric. It also allows for the inclusion of missing values and the specification of data dimensions.} + +\examples{ +ncores <- 2 +ngpus <- 0 +computation <- "exact" +hardware <- new(Hardware, computation, ncores, ngpus) + +problem_size <- 4 +dimension = "2D" +dts <- 2 +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) +x_locations <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) +y_locations <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) +empty_data <- new(Data, problem_size, dimension) + +fisher(kernel = "univariate_matern_stationary" , estimated_theta = c(1,0.1,0.5), dts = 2 , z_miss = 3, data=empty_data, matrix = z_value, x = x_locations, y = y_locations) +} diff --git a/man/get_Z_measurement_vector.Rd b/man/get_Z_measurement_vector.Rd new file mode 100644 index 00000000..44fa85b4 --- /dev/null +++ b/man/get_Z_measurement_vector.Rd @@ -0,0 +1,50 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file get_Z_measurement_vector.Rd +% @brief roxygen2 documentation for the R wrapper of get_Z_measurment_vector function. +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{get_Z_measurement_vector} +\alias{get_Z_measurement_vector} +\title{Get descriptive Z values from ExaGeoStat data} + +\description{ +Retrieves descriptive Z values from ExaGeoStat data based on type. +} + +\usage{ +get_Z_measurement_vector(data,type) +} + +\arguments{ +\item{data}{A list of ExaGeoStatData that contains the locations. +\item{type}{A string specifying the type of descriptor value to retrieve (e.g., "Chameleon", "HiCMA").} +} + +\value{ +A numeric vector of descriptive Z values. +} + +\examples{ +ncores <- 2 +ngpus <- 0 +computation <- "exact" +hardware <- new(Hardware, computation, ncores, ngpus) + +dimension = "3D" +problem_size <- 4 +empty_data <- new(Data, problem_size, dimension) + +dts <- 2 +kernel <- "univariate_matern_stationary" +initial_theta <- c(1,0.1,0.5) + +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) + +Z <- get_Z_measurement_vector(data=exageostat_data, type="chameleon") +} diff --git a/man/get_locationsX.Rd b/man/get_locationsX.Rd new file mode 100644 index 00000000..016c1af9 --- /dev/null +++ b/man/get_locationsX.Rd @@ -0,0 +1,49 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file get_locationsX.Rd +% @brief roxygen2 documentation for the R wrapper of get_locationsX function for X coordinates. +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{get_locationsX} +\alias{get_locationsX} +\title{Get X Locations} + +\description{ +Retrieves X coordinates of locations from ExaGeoStatData object. +} + +\usage{ +get_locationsX(data) +} + +\arguments{ +\item{data}{A list of ExaGeoStatData that contains the locations.} +} + +\value{ +A numeric vector of X locations. +} + +\examples{ +ncores <- 2 +ngpus <- 0 +computation <- "exact" +hardware <- new(Hardware, computation, ncores, ngpus) + +dimension = "2D" +problem_size <- 4 +empty_data <- new(Data, problem_size, dimension) + +dts <- 2 +kernel <- "univariate_matern_stationary" +initial_theta <- c(1,0.1,0.5) + +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) + +x <- get_locationsX(data=exageostat_data) +} diff --git a/man/get_locationsY.Rd b/man/get_locationsY.Rd new file mode 100644 index 00000000..839e4d52 --- /dev/null +++ b/man/get_locationsY.Rd @@ -0,0 +1,49 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file get_locationsY.Rd +% @brief roxygen2 documentation for the R wrapper of get_locationsY function for Y coordinates. +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{get_locationsY} +\alias{get_locationsY} +\title{Get Y Locations} + +\description{ +This function retrieves the Y locations from the provided data. +} + +\usage{ +get_locationsY(data) +} + +\arguments{ +\item{data}{A list of ExaGeoStatData that contains the locations.} +} + +\value{ +A numeric vector of Y locations. +} + +\examples{ +ncores <- 2 +ngpus <- 0 +computation <- "exact" +hardware <- new(Hardware, computation, ncores, ngpus) + +dimension = "2D" +problem_size <- 4 +empty_data <- new(Data, problem_size, dimension) + +dts <- 2 +kernel <- "univariate_matern_stationary" +initial_theta <- c(1,0.1,0.5) + +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) + +y <- get_locationsY(data=exageostat_data) +} diff --git a/man/get_locationsZ.Rd b/man/get_locationsZ.Rd new file mode 100644 index 00000000..ee8f418a --- /dev/null +++ b/man/get_locationsZ.Rd @@ -0,0 +1,49 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file get_locationsZ.Rd +% @brief roxygen2 documentation for the R wrapper of get_locationsZ function for Z coordinates. +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{get_locationsZ} +\alias{get_locationsZ} +\title{Get Z Locations} + +\description{ +Retrieves Z coordinates of locations from ExaGeoStatData object. +} + +\usage{ +get_locationsZ(data) +} + +\arguments{ +\item{data}{A list of ExaGeoStatData that contains the locations.} +} + +\value{ +A numeric vector of Z locations. +} + +\examples{ +ncores <- 2 +ngpus <- 0 +computation <- "exact" +hardware <- new(Hardware, computation, ncores, ngpus) + +dimension = "3D" +problem_size <- 4 +empty_data <- new(Data, problem_size, dimension) + +dts <- 2 +kernel <- "univariate_matern_stationary" +initial_theta <- c(1,0.1,0.5) + +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) + +z <- get_locationsZ(data=exageostat_data) +} diff --git a/man/idw.Rd b/man/idw.Rd new file mode 100644 index 00000000..8a7f0838 --- /dev/null +++ b/man/idw.Rd @@ -0,0 +1,98 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file idw.Rd +% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatIDW function. +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{idw} +\alias{idw} +\title{This function performs IDW interpolation for a given dataset and theta vector.} + +\usage{ +idw(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", z_miss, data, matrix = R_NilValue, x = R_NilValue, y = R_NilValue, z = R_NilValue) +} + +\arguments{ + \item{kernel}{A string specifying the kernel to use - available kernels + ( "BivariateMaternFlexible", + "BivariateMaternParsimonious", + "BivariateSpacetimeMaternStationary", + "TrivariateMaternParsimonious", + "UnivariateExpNonGaussian", + "UnivariateMaternDbeta", + "UnivariateMaternDdbetaBeta", + "UnivariateMaternDdbetaNu", + "UnivariateMaternDdnuNu", + "UnivariateMaternDdsigmaSquare", + "UnivariateMaternDdsigmaSquareBeta", + "UnivariateMaternDdsigmaSquareNu", + "UnivariateMaternDnu", + "UnivariateMaternDsigmaSquare", + "UnivariateMaternNonGaussian", + "UnivariateMaternNuggetsStationary", + "UnivariateMaternStationary", + "UnivariatePowExpStationary", + "UnivariateSpacetimeMaternStationary", + "bivariate_matern_flexible", + "bivariate_matern_parsimonious", + "bivariate_spacetime_matern_stationary", + "trivariate_matern_parsimonious", + "univariate_exp_non_gaussian", + "univariate_matern_dbeta", + "univariate_matern_ddbeta_beta", + "univariate_matern_ddbeta_nu", + "univariate_matern_ddnu_nu", + "univariate_matern_ddsigma_square", + "univariate_matern_ddsigma_square_beta", + "univariate_matern_ddsigma_square_nu", + "univariate_matern_dnu", + "univariate_matern_dsigma_square", + "univariate_matern_non_gaussian", + "univariate_matern_nuggets_stationary", + "univariate_matern_stationary", + "univariate_pow_exp_stationary", + "univariate_spacetime_matern_stationary" + )} + \item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} + \item{estimated_theta}{A list of estimated theta parameters} + \item{dts}{A numeric value representing the time step size} + \item{lts}{A numeric value representing the length step size. Default is 0} + \item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D"} + \item{z_miss}{ A numeric value representing the missing value indicator} + \item{data}{ A list of data vectors} + \item{matrix}{ A matrix object. Default is `R_NilValue`} + \item{x}{A numeric vector. Default is `R_NilValue`} + \item{y}{A numeric vector. Default is `R_NilValue`} + \item{z}{A numeric vector. Default is `R_NilValue`} +} + +\value{ + A vector containing the IDW data. +} + +\description{ +This function performs Inverse Distance Weighting (IDW) interpolation for a given dataset and theta vector. +} + +\examples{ +ncores <- 2 +ngpus <- 0 +computation <- "exact" + +hardware <- new(Hardware, computation, ncores, ngpus) + +problem_size <- 4 +dimension = "2D" +dts <- 2 +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) +x_locations <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) +y_locations <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) + +empty_data <- new(Data, problem_size, dimension) +idw(kernel = "univariate_matern_stationary" , estimated_theta = c(1,0.1,0.5), dts = 2 , z_miss = 3, data=empty_data, matrix = z_value, x = x_locations, y = y_locations) +} diff --git a/man/mloe_mmom.Rd b/man/mloe_mmom.Rd new file mode 100644 index 00000000..b805ba2f --- /dev/null +++ b/man/mloe_mmom.Rd @@ -0,0 +1,98 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file mloe_mmom.Rd +% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatMLOE_MMOM function +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{mloe_mmom} +\alias{mloe_mmom} +\title{Mean Misspecification of the Mean Square Error (MMOM) and Mean Loss of Efficiency (MLOE) using exact method.} + +\usage{ +mloe_mmom(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, true_theta=estimated_theta, z_miss=3 ) +} + +\arguments{ + \item{kernel}{A string specifying the kernel to use - available kernels + ( "BivariateMaternFlexible", + "BivariateMaternParsimonious", + "BivariateSpacetimeMaternStationary", + "TrivariateMaternParsimonious", + "UnivariateExpNonGaussian", + "UnivariateMaternDbeta", + "UnivariateMaternDdbetaBeta", + "UnivariateMaternDdbetaNu", + "UnivariateMaternDdnuNu", + "UnivariateMaternDdsigmaSquare", + "UnivariateMaternDdsigmaSquareBeta", + "UnivariateMaternDdsigmaSquareNu", + "UnivariateMaternDnu", + "UnivariateMaternDsigmaSquare", + "UnivariateMaternNonGaussian", + "UnivariateMaternNuggetsStationary", + "UnivariateMaternStationary", + "UnivariatePowExpStationary", + "UnivariateSpacetimeMaternStationary", + "bivariate_matern_flexible", + "bivariate_matern_parsimonious", + "bivariate_spacetime_matern_stationary", + "trivariate_matern_parsimonious", + "univariate_exp_non_gaussian", + "univariate_matern_dbeta", + "univariate_matern_ddbeta_beta", + "univariate_matern_ddbeta_nu", + "univariate_matern_ddnu_nu", + "univariate_matern_ddsigma_square", + "univariate_matern_ddsigma_square_beta", + "univariate_matern_ddsigma_square_nu", + "univariate_matern_dnu", + "univariate_matern_dsigma_square", + "univariate_matern_non_gaussian", + "univariate_matern_nuggets_stationary", + "univariate_matern_stationary", + "univariate_pow_exp_stationary", + "univariate_spacetime_matern_stationary" + )} +\item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} +\item{estimated_theta}{A list of estimated theta parameters} +\item{dts}{A numeric value representing the time step size} +\item{lts}{A numeric value representing the length step size. Default is 0} +\item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D"} +\item{z_miss}{ A numeric value representing the missing value indicator} +\item{data}{ A list of data vectors} +\item{matrix}{ A matrix object. Default is `R_NilValue`} +\item{x}{A numeric vector. Default is `R_NilValue`} +\item{y}{A numeric vector. Default is `R_NilValue`} +\item{z}{A numeric vector. Default is `R_NilValue`} +} + +\value{ +A vector of MLOE/MMOM values +} + +\description{ +This function calculates Mean Misspecification of the Mean Square Error (MMOM) and Mean Loss of Efficiency (MLOE). +} + +\examples{ +ncores <- 2 +ngpus <- 0 +computation <- "exact" + +hardware <- new(Hardware, computation, ncores, ngpus) + +problem_size <- 4 +dimension = "2D" +dts <- 2 +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) +x_locations <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) +y_locations <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) + +empty_data <- new(Data, problem_size, dimension) +mloe_mmom(data=empty_data, matrix=z_value, x=x_locations, y=y_locations, kernel="univariate_matern_stationary", dts = 2, estimated_theta=c(1,0.1,0.5), true_theta=c(1,0.1,0.5), z_miss=3 ) +} diff --git a/man/model_data.Rd b/man/model_data.Rd new file mode 100644 index 00000000..4346d0bd --- /dev/null +++ b/man/model_data.Rd @@ -0,0 +1,107 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file model_data.Rd +% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatModelData function. +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{model_data} +\alias{model_data} +\title{This function models data based on the provided computation method, kernel, distance matrix, and other parameters.} + +\usage{ +model_data(computation = "exact", kernel, distance_matrix = "euclidean", lb, ub, tol = 4, mle_itr, dts, lts = 0, dimension = "2D", data, matrix = R_NilValue, x = R_NilValue, y = R_NilValue, z = R_NilValue) +} + +\arguments{ +\item{computation}{A string specifying the computation method, either "exact" or another method supported by the package. Default is "exact".} +\item{kernel}{A string specifying the kernel to use - available kernels + ( "BivariateMaternFlexible", + "BivariateMaternParsimonious", + "BivariateSpacetimeMaternStationary", + "TrivariateMaternParsimonious", + "UnivariateExpNonGaussian", + "UnivariateMaternDbeta", + "UnivariateMaternDdbetaBeta", + "UnivariateMaternDdbetaNu", + "UnivariateMaternDdnuNu", + "UnivariateMaternDdsigmaSquare", + "UnivariateMaternDdsigmaSquareBeta", + "UnivariateMaternDdsigmaSquareNu", + "UnivariateMaternDnu", + "UnivariateMaternDsigmaSquare", + "UnivariateMaternNonGaussian", + "UnivariateMaternNuggetsStationary", + "UnivariateMaternStationary", + "UnivariatePowExpStationary", + "UnivariateSpacetimeMaternStationary", + "bivariate_matern_flexible", + "bivariate_matern_parsimonious", + "bivariate_spacetime_matern_stationary", + "trivariate_matern_parsimonious", + "univariate_exp_non_gaussian", + "univariate_matern_dbeta", + "univariate_matern_ddbeta_beta", + "univariate_matern_ddbeta_nu", + "univariate_matern_ddnu_nu", + "univariate_matern_ddsigma_square", + "univariate_matern_ddsigma_square_beta", + "univariate_matern_ddsigma_square_nu", + "univariate_matern_dnu", + "univariate_matern_dsigma_square", + "univariate_matern_non_gaussian", + "univariate_matern_nuggets_stationary", + "univariate_matern_stationary", + "univariate_pow_exp_stationary", + "univariate_spacetime_matern_stationary" + )} +\item{distance_matrix}{A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} +\item{lb}{A numeric value representing the lower bound for the computation.} +\item{ub}{A numeric value representing the upper bound for the computation.} +\item{tol}{A numeric value specifying the tolerance for the computation. Default is 4.} +\item{mle_itr}{A numeric value specifying the maximum number of iterations for the computation.} +\item{dts}{A numeric value representing the time step size.} +\item{lts}{A numeric value representing the length step size. Default is 0.} +\item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D".} +\item{data}{A list of data vectors.} +\item{matrix}{A matrix object. Default is `R_NilValue`.} +\item{x}{A numeric vector. Default is `R_NilValue`.} +\item{y}{A numeric vector. Default is `R_NilValue`.} +\item{z}{A numeric vector. Default is `R_NilValue`.} +} + +\value{ +A vector containing the starting theta. +} + +\description{ +This function models data based on the provided computation method, kernel, distance matrix, and other parameters. +} + +\examples{ +ncores <- 2 +ngpus <- 0 +computation <- "exact" +hardware <- new(Hardware, computation, ncores, ngpus) + +dimension = "2D" +problem_size <- 4 + +empty_data <- new(Data, problem_size, dimension) + +dts <- 2 +kernel <- "univariate_matern_stationary" +lower_bound <- c(0.1,0.1,0.1) +upper_bound <- c(5,5,5) + +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) + +theta <- model_data(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, + dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10, computation=computation) +} diff --git a/man/predict_data.Rd b/man/predict_data.Rd new file mode 100644 index 00000000..233c06c9 --- /dev/null +++ b/man/predict_data.Rd @@ -0,0 +1,100 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file predict_data.Rd +% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatPredictData function. +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{predict_data} +\alias{predict_data} +\title{This function predicts data based on the provided kernel, distance matrix, estimated theta, and other parameters.} + +\usage{ +predict_data(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", z_miss, data, matrix = R_NilValue, x = R_NilValue, y = R_NilValue, z = R_NilValu) +} + +\arguments{ + \item{kernel}{A string specifying the kernel to use - available kernels + ( "BivariateMaternFlexible", + "BivariateMaternParsimonious", + "BivariateSpacetimeMaternStationary", + "TrivariateMaternParsimonious", + "UnivariateExpNonGaussian", + "UnivariateMaternDbeta", + "UnivariateMaternDdbetaBeta", + "UnivariateMaternDdbetaNu", + "UnivariateMaternDdnuNu", + "UnivariateMaternDdsigmaSquare", + "UnivariateMaternDdsigmaSquareBeta", + "UnivariateMaternDdsigmaSquareNu", + "UnivariateMaternDnu", + "UnivariateMaternDsigmaSquare", + "UnivariateMaternNonGaussian", + "UnivariateMaternNuggetsStationary", + "UnivariateMaternStationary", + "UnivariatePowExpStationary", + "UnivariateSpacetimeMaternStationary", + "bivariate_matern_flexible", + "bivariate_matern_parsimonious", + "bivariate_spacetime_matern_stationary", + "trivariate_matern_parsimonious", + "univariate_exp_non_gaussian", + "univariate_matern_dbeta", + "univariate_matern_ddbeta_beta", + "univariate_matern_ddbeta_nu", + "univariate_matern_ddnu_nu", + "univariate_matern_ddsigma_square", + "univariate_matern_ddsigma_square_beta", + "univariate_matern_ddsigma_square_nu", + "univariate_matern_dnu", + "univariate_matern_dsigma_square", + "univariate_matern_non_gaussian", + "univariate_matern_nuggets_stationary", + "univariate_matern_stationary", + "univariate_pow_exp_stationary", + "univariate_spacetime_matern_stationary" + )} + \item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} + \item{estimated_theta}{A list of estimated theta parameters} + \item{dts}{A numeric value representing the time step size} + \item{lts}{A numeric value representing the length step size. Default is 0} + \item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D"} + \item{z_miss}{ A numeric value representing the missing value indicator} + \item{data}{ A list of data vectors} + \item{matrix}{ A matrix object. Default is `R_NilValue`} + \item{x}{A numeric vector. Default is `R_NilValue`} + \item{y}{A numeric vector. Default is `R_NilValue`} + \item{z}{A numeric vector. Default is `R_NilValue`} +} + +\value{ + N/A + } + +\description{ +This function predicts data based on the provided kernel, distance matrix, estimated theta, and other parameters. +} + +\examples{ +dimension = "2D" +ncores <- 2 +ngpus <- 0 +problem_size <- 4 +dts <- 2 +lts <- 0 +computation <- "exact" +kernel <- "univariate_matern_stationary" +estimated_theta <- c(1,0.1,0.5) +hardware <- new(Hardware, computation, ncores, ngpus) + +empty_data <- new(Data, problem_size, dimension) +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) + +predict_data(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, z_miss=3) +} diff --git a/man/simulate_data.Rd b/man/simulate_data.Rd new file mode 100644 index 00000000..4ab8e201 --- /dev/null +++ b/man/simulate_data.Rd @@ -0,0 +1,100 @@ + +% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% All rights reserved. +% ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +% @file simulate_data.Rd +% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatLoadData function +% @version 1.1.0 +% @author Mahmoud ElKarargy +% @date 2024-03-17 + +\name{simulate_data} +\alias{simulate_data} +\title{This function simulates data based on the provided computation method, kernel, distance matrix, and other parameters.} + +\usage{ +simulate_data(kernel, initial_theta, distance_matrix = "euclidean", problem_size, seed = 0, dts, lts = 0, dimension = "2D", log_path = "", data_path = "", observations_file = "", recovery_file = "") +} + +\arguments{ + \item{kernel}{A string specifying the kernel to use - available kernels + ( "BivariateMaternFlexible", + "BivariateMaternParsimonious", + "BivariateSpacetimeMaternStationary", + "TrivariateMaternParsimonious", + "UnivariateExpNonGaussian", + "UnivariateMaternDbeta", + "UnivariateMaternDdbetaBeta", + "UnivariateMaternDdbetaNu", + "UnivariateMaternDdnuNu", + "UnivariateMaternDdsigmaSquare", + "UnivariateMaternDdsigmaSquareBeta", + "UnivariateMaternDdsigmaSquareNu", + "UnivariateMaternDnu", + "UnivariateMaternDsigmaSquare", + "UnivariateMaternNonGaussian", + "UnivariateMaternNuggetsStationary", + "UnivariateMaternStationary", + "UnivariatePowExpStationary", + "UnivariateSpacetimeMaternStationary", + "bivariate_matern_flexible", + "bivariate_matern_parsimonious", + "bivariate_spacetime_matern_stationary", + "trivariate_matern_parsimonious", + "univariate_exp_non_gaussian", + "univariate_matern_dbeta", + "univariate_matern_ddbeta_beta", + "univariate_matern_ddbeta_nu", + "univariate_matern_ddnu_nu", + "univariate_matern_ddsigma_square", + "univariate_matern_ddsigma_square_beta", + "univariate_matern_ddsigma_square_nu", + "univariate_matern_dnu", + "univariate_matern_dsigma_square", + "univariate_matern_non_gaussian", + "univariate_matern_nuggets_stationary", + "univariate_matern_stationary", + "univariate_pow_exp_stationary", + "univariate_spacetime_matern_stationary" + )} +\item{initial_theta}{A list of initial theta parameters.} +\item{distance_matrix}{A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} +\item{problem_size}{A numeric value representing the size of the problem to simulate.} +\item{seed A numeric}{value specifying the seed for random number generation. Default is 0.} +\item{dts A numeric}{value representing the time step size.} +\item{lts A numeric}{value representing the length step size. Default is 0.} +\item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D".} +\item{log_path}{A string specifying the path for logging. Default is "".} +\item{data_path}{A string specifying the path for data storage. Default is "".} +\item{observations_file}{A string specifying the file name for observations. Default is "".} +\item{recovery_file}{A string specifying the file name for recovery. Default is "".} +} + +\value{ +A pointer to ExaGeoStatData object that contains the loaded data. +} + +\description{ +This function loads data into an ExaGeoStatData object using the provided configuration and computational settings. +} + +\examples{ +ncores <- 2 +ngpus <- 0 +computation <- "exact" +hardware <- new(Hardware, computation, ncores, ngpus) + +dimension = "2D" +problem_size <- 4 +empty_data <- new(Data, problem_size, dimension) + +dts <- 2 +kernel <- "univariate_matern_stationary" +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) +initial_theta <- c(1,0.1,0.5) + +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a7e5800e..5a8470ac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,6 +22,7 @@ add_subdirectory(kernels) add_subdirectory(linear-algebra-solvers) add_subdirectory(prediction) add_subdirectory(results) +add_subdirectory(runtime) if (USE_R) add_subdirectory(Rcpp-adapters) diff --git a/src/Rcpp-adapters/FunctionsAdapter.cpp b/src/Rcpp-adapters/FunctionsAdapter.cpp index 259e349b..1161a87d 100644 --- a/src/Rcpp-adapters/FunctionsAdapter.cpp +++ b/src/Rcpp-adapters/FunctionsAdapter.cpp @@ -11,296 +11,369 @@ * @date 2024-01-29 **/ -#include - #include -#include #include +#include using namespace std; using namespace Rcpp; -using namespace exageostat::dataunits; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::results; +using namespace exageostat::configurations; namespace exageostat::adapters { - Configurations * - R_InitializeArguments(const int &aProblemSize, const string &aKernelName, const vector &aTileSize, - const vector &aP_QGrid, const int &aTimeSlot, const string &aComputation, - const string &aPrecision, const vector &aCoresGPUsNumber, const int &aBand, - const int &aMaxRank, const vector &aInitialTheta, - const vector > &aLowerUpperBounds, const vector &aEstimatedTheta, - const string &aVerbose, const string &aDimension, const int &aMaxMleIterations, - const int &aTolerance, const vector &aPrediction, const std::vector &aPath, const bool &aSaveData) { - - vector argStrings; - - // Program name or placeholder - argStrings.emplace_back("--ConfigurationsModuleR"); - // Convert each argument to string and add to argv - argStrings.push_back("--N=" + to_string(aProblemSize)); - argStrings.push_back("--kernel=" + aKernelName); - argStrings.push_back("--dts=" + to_string(aTileSize[0])); - argStrings.push_back("--lts=" + to_string(aTileSize[1])); - argStrings.push_back("--p=" + to_string(aP_QGrid[0])); - argStrings.push_back("--q=" + to_string(aP_QGrid[1])); - argStrings.push_back("--time_slot=" + to_string(aTimeSlot)); - argStrings.push_back("--computation=" + aComputation); - argStrings.push_back("--precision=" + aPrecision); - argStrings.push_back("--cores=" + to_string(aCoresGPUsNumber[0])); - argStrings.push_back("--gpus=" + to_string(aCoresGPUsNumber[1])); - argStrings.push_back("--band=" + to_string(aBand)); - argStrings.push_back("--max_rank=" + to_string(aMaxRank)); - - string initial_theta = "--iTheta="; - for(int i = 0; i < aInitialTheta.size(); i++){ - if (i != aInitialTheta.size() - 1){ - initial_theta += to_string(aInitialTheta[i]) + ":"; - } else{ - initial_theta += to_string(aInitialTheta[i]); - } - } - argStrings.push_back(initial_theta); - string lower_bound = "--lb="; - string upper_bound = "--ub="; - for(int i = 0; i < aLowerUpperBounds[0].size(); i++){ - if (i != aLowerUpperBounds[0].size() - 1) { - lower_bound += to_string(aLowerUpperBounds[0][i]) + ":"; - } - else{ - lower_bound += to_string(aLowerUpperBounds[0][i]); - } - } - for(int i = 0; i < aLowerUpperBounds[1].size(); i++){ - if (i != aLowerUpperBounds[1].size() - 1) { - upper_bound += to_string(aLowerUpperBounds[1][i]) + ":"; - } - else{ - upper_bound += to_string(aLowerUpperBounds[1][i]); - } - } - argStrings.push_back(lower_bound); - argStrings.push_back(upper_bound); - - string estimated_theta = "--eTheta="; - for(int i = 0; i < aInitialTheta.size(); i++){ - if (i != aInitialTheta.size() - 1) { - if(i < aEstimatedTheta.size()){ - estimated_theta += to_string(aEstimatedTheta[i]) + ":"; - } - else{ - estimated_theta += "-1:"; - } - } - else{ - if(i < aEstimatedTheta.size()) { - estimated_theta += to_string(aEstimatedTheta[i]); - } - else{ - estimated_theta += "-1"; - } - } - } - argStrings.push_back(estimated_theta); - argStrings.push_back("--verbose=" + aVerbose); - argStrings.push_back("--dimension=" + aDimension); - argStrings.push_back("--max_mle_iterations=" + to_string(aMaxMleIterations)); - argStrings.push_back("--tolerance=" + to_string(aTolerance)); - if(!aPath[0].empty()){ - argStrings.push_back("--log_path=" + aPath[0]); - } - if(!aPath[1].empty()){ - argStrings.push_back("--data_path=" + aPath[1]); - } - if(!aPath[2].empty()){ - argStrings.push_back("--observations_file=" + aPath[2]); - } - if(!aPath[3].empty()){ - argStrings.push_back("--recovery_file=" + aPath[3]); - } - if(aSaveData){ - argStrings.emplace_back("--log"); - } - // This means that ZMiss > 0, which means prediction is activated - if (aPrediction[0] > 0) { - argStrings.push_back("--ZMiss=" + to_string(aPrediction[0])); - - // if mspe is activated - if (aPrediction[1]) { - argStrings.emplace_back("--mspe"); - } - - // if idw is activated - if (aPrediction[2]) { - argStrings.emplace_back("--idw"); - } - - // if fisher is activated - if (aPrediction[3]) { - argStrings.emplace_back("--fisher"); - } - - // if mloe_mmom is activated - if (aPrediction[4]) { - argStrings.emplace_back("--mloe_mmom"); - } - } - // Now, allocate a char** array and copy the arguments into it. - // Argv ownership is passed to Configurations, and it's responsibility for freeing it. - char **argv = new char *[argStrings.size()]; - - for (size_t i = 0; i < argStrings.size(); ++i) { - argv[i] = new char[argStrings[i].size() + 1]; // +1 for the null terminator - std::strcpy(argv[i], argStrings[i].c_str()); - } - - auto configurations = new Configurations(); - configurations->InitializeArguments(argStrings.size(), argv, true); - return configurations; - } + NumericVector R_GetLocationX(ExaGeoStatData *apData) { - NumericVector R_GetLocationX(ExaGeoStatData *apData){ // Obtain the pointer to the array of doubles - double* data = apData->GetLocations()->GetLocationX(); + double *data = apData->GetLocations()->GetLocationX(); int length = apData->GetLocations()->GetSize(); // Create an empty NumericVector of the appropriate length NumericVector vec(length); - // Copy data from the double array to the NumericVector - std::copy(data, data + length, vec.begin()); - + copy(data, data + length, vec.begin()); return vec; } - NumericVector R_GetLocationY(ExaGeoStatData *apData){ + NumericVector R_GetLocationY(ExaGeoStatData *apData) { // Obtain the pointer to the array of doubles - double* data = apData->GetLocations()->GetLocationY(); + double *data = apData->GetLocations()->GetLocationY(); int length = apData->GetLocations()->GetSize(); // Create an empty NumericVector of the appropriate length NumericVector vec(length); - // Copy data from the double array to the NumericVector - std::copy(data, data + length, vec.begin()); - + copy(data, data + length, vec.begin()); return vec; } - NumericVector R_GetLocationZ(ExaGeoStatData *apData){ + NumericVector R_GetLocationZ(ExaGeoStatData *apData) { // Obtain the pointer to the array of doubles - double* data = apData->GetLocations()->GetLocationZ(); + double *data = apData->GetLocations()->GetLocationZ(); int length = apData->GetLocations()->GetSize(); // Create an empty NumericVector of the appropriate length NumericVector vec(length); - // Copy data from the double array to the NumericVector - std::copy(data, data + length, vec.begin()); - + copy(data, data + length, vec.begin()); return vec; } - NumericVector R_GetDescZValues(ExaGeoStatData *apData, const std::string &aType){ + NumericVector R_GetDescZValues(ExaGeoStatData *apData, const string &aType) { + DescriptorType descriptorType; void *pDescriptor; - if(aType == "chameleon" || aType == "Chameleon"){ + + if (aType == "chameleon" || aType == "Chameleon") { descriptorType = CHAMELEON_DESCRIPTOR; pDescriptor = apData->GetDescriptorData()->GetDescriptor(descriptorType, DESCRIPTOR_Z).chameleon_desc; - } - else if (aType == "hicma" || aType == "Hicma" || aType == "HICMA"){ + } else if (aType == "hicma" || aType == "Hicma" || aType == "HICMA") { #ifdef USE_HICMA descriptorType = HICMA_DESCRIPTOR; pDescriptor = apData->GetDescriptorData()->GetDescriptor(descriptorType, DESCRIPTOR_Z).hicma_desc; #else throw runtime_error("Please enable HiCMA to use HiCMA descriptors."); #endif - } - else { + } else { throw domain_error("Invalid type of descriptor, please use chameleon or hicma."); } + // Obtain the pointer to the array of doubles - double* data = apData->GetDescriptorData()->GetDescriptorMatrix(descriptorType, pDescriptor); + double *data = apData->GetDescriptorData()->GetDescriptorMatrix(descriptorType, pDescriptor); int length = apData->GetLocations()->GetSize(); // Create an empty NumericVector of the appropriate length NumericVector vec(length); - // Copy data from the double array to the NumericVector - std::copy(data, data + length, vec.begin()); - + copy(data, data + length, vec.begin()); return vec; } - ExaGeoStatData *R_ExaGeoStatLoadData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, - ExaGeoStatData *apData) { + ExaGeoStatData * + R_ExaGeoStatLoadData(const string &aKernelName, const vector &aInitialTheta, const string &aDistanceMatrix, + const int &aProblemSize, const int &aSeed, const int &aDenseTileSize, const int &aLowTileSize, + const string &aDimension, const string &aLogPath, const string &aDataPath, + const string &aRecoveryFilePath, const string &aObservationsFilePath) { + + // manually set the configurations values with the user input from R. + Configurations configurations; + configurations.SetProblemSize(aProblemSize); + configurations.CheckKernelValue(aKernelName); + configurations.ParseDistanceMetric(aDistanceMatrix); + configurations.SetSeed(aSeed); + configurations.SetDenseTileSize(aDenseTileSize); + configurations.SetLowTileSize(aLowTileSize); + configurations.SetComputation(EXACT_DENSE); + configurations.SetInitialTheta(aInitialTheta); + configurations.SetDimension(Configurations::CheckDimensionValue(aDimension)); + + if (!aLogPath.empty()) { + configurations.SetLogger(true); + configurations.SetLoggerPath(aLogPath); + } + if (!aDataPath.empty()) { + configurations.SetDataPath(aDataPath); + configurations.SetIsSynthetic(false); + } + configurations.SetRecoveryFile(aRecoveryFilePath); + configurations.SetActualObservationsFilePath(aObservationsFilePath); - // Wrap the raw pointer in a unique_ptr - std::unique_ptr> apDataPtr(apData); - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(*apHardware, *apConfigurations, apDataPtr); + unique_ptr> data; + ExaGeoStat::ExaGeoStatLoadData(configurations, data); - // Since ExaGeoStatLoadData takes a reference, apDataPtr still owns the object. - // We can safely return the raw pointer, but we need to release it from apDataPtr to avoid deletion. - return apDataPtr.release(); + // We can safely return the raw pointer, but we need to release it from data to avoid deletion. + return data.release(); } - ExaGeoStatData *R_ExaGeoStatModelData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, - ExaGeoStatData *apData, - Nullable aMeasurementsVector, - Nullable aLocationsX, - Nullable aLocationsY, - Nullable aLocationsZ) { - - std::unique_ptr> apDataPtr(apData); - double* pMeasurementsVectorPtr = nullptr; - if (aMeasurementsVector == nullptr || aMeasurementsVector.isNull()){ - // This was the only way to pass C++ tests, if we checked for aMeasurementsVector != nullptr a seg fault occurs + vector + R_ExaGeoStatModelData(const string &aComputation, const string &aKernelName, const string &aDistanceMatrix, + const vector &aLowerBound, const vector &aUpperBound, const int &aTolerance, + const int &aMleIterations, const int &aDenseTileSize, const int &aLowTileSize, + const string &aDimension, const int &aBand, const int &aMaxRank, + SEXP apData, Nullable aMeasurementsVector, + Nullable aLocationsX, Nullable aLocationsY, + Nullable aLocationsZ) { + + Configurations configurations; + bool is_initialized = ((SEXP) apData == R_NilValue); + unique_ptr> data; + if (!is_initialized) { + auto temp_data = (ExaGeoStatData *) Rcpp::internal::as_module_object_internal(apData); + // Set the unique_ptr to manage the ExaGeoStatData pointer + data.reset(temp_data); + } else { + NumericVector z_values(aMeasurementsVector.get()); + + // Create a new ExaGeoStatData object with configurations and manage it with unique_ptr + data = make_unique>(z_values.size(), configurations.GetDimension()); } - else{ - NumericVector mat(aMeasurementsVector.get()); - pMeasurementsVectorPtr = &mat[0]; // Get the raw pointer to the matrix data + double *pMeasurementsVectorPtr = GetDataFromArguments(aMeasurementsVector, aLocationsX, aLocationsY, + aLocationsZ, data, configurations, + aKernelName, aDistanceMatrix, aDenseTileSize, + aLowTileSize, aDimension, + Configurations::CheckComputationValue(aComputation)); + + configurations.SetLowerBounds(aLowerBound); + configurations.SetUpperBounds(aUpperBound); + configurations.SetMaxMleIterations(aMleIterations); + configurations.SetTolerance(aTolerance); + configurations.SetBand(aBand); + configurations.SetMaxRank(aMaxRank); + + ExaGeoStat::ExaGeoStatDataModeling(configurations, data, pMeasurementsVectorPtr); + // Take back ownership, to avoid deleting apData when the unique_ptr goes out of scope. + apData = (SEXP) data.release(); + return configurations.GetStartingTheta(); + } - if (!aLocationsX.isNull()) { - double *pLocationsXPtr; - NumericVector mat_location(aLocationsX.get()); - pLocationsXPtr = &mat_location[0]; // Get the raw pointer to the matrix data - apData->GetLocations()->SetLocationX(*pLocationsXPtr, mat_location.size()); + vector R_ExaGeoStatPredictData(const string &aKernelName, const string &aDistanceMatrix, + const vector &aEstimatedTheta, + const int &aDenseTileSize, const int &aLowTileSize, + const string &aDimension, + vector> &aTrainData, + vector> &aTestData) { + + Configurations configurations; + configurations.SetComputation(EXACT_DENSE); + + auto data = make_unique>(aTrainData[0].size(), configurations.GetDimension()); + auto *train_locations = new dataunits::Locations(aTrainData[0].size(), configurations.GetDimension()); + auto *test_locations = new dataunits::Locations(aTestData[0].size(), configurations.GetDimension()); + + auto* z_values = new double[aTrainData.back().size()]; + for(int i = 0; i< aTrainData[0].size(); i++){ + train_locations->SetLocationX(*aTrainData[0].data(), aTrainData[0].size()); + train_locations->SetLocationY(*aTrainData[1].data(), aTrainData[1].size()); + if(aTrainData.size() > 3) { + train_locations->SetLocationZ(*aTrainData[2].data(), aTrainData[2].size()); } + } + memcpy(z_values, aTrainData.back().data(), aTrainData.back().size() * sizeof(double)); + for(int i = 0; i< aTestData[0].size(); i++){ + test_locations->SetLocationX(*aTestData[0].data(), aTestData[0].size()); + test_locations->SetLocationY(*aTestData[1].data(), aTestData[1].size()); + } - if (aLocationsY.isNotNull()) { - double *pLocationsYPtr; - NumericVector mat_location(aLocationsY.get()); - pLocationsYPtr = &mat_location[0]; // Get the raw pointer to the matrix data - apData->GetLocations()->SetLocationY(*pLocationsYPtr, mat_location.size()); + // Set common configurations. + configurations.CheckKernelValue(aKernelName); + configurations.ParseDistanceMetric(aDistanceMatrix); + configurations.SetDenseTileSize(aDenseTileSize); + configurations.SetLowTileSize(aLowTileSize); + configurations.SetDimension(Configurations::CheckDimensionValue(aDimension)); + configurations.SetProblemSize(data->GetLocations()->GetSize()); + configurations.SetEstimatedTheta(aEstimatedTheta); + configurations.SetIsMSPE(TRUE); + + ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_values, train_locations, test_locations); + // Take back ownership, to avoid deleting apData when the unique_ptr goes out of scope. + auto temp_data = data.release(); + delete[] z_values; + + return Results::GetInstance()->GetPredictedMissedValues(); + } + vector R_ExaGeoStatMLOE_MMOM(const string &aKernelName, const string &aDistanceMatrix, + const vector &aEstimatedTheta, const vector &aTrueTheta, + const int &aDenseTileSize, const int &aLowTileSize, const string &aDimension, + vector> &aTrainData, + vector> &aTestData) { + + Configurations configurations; + configurations.SetComputation(EXACT_DENSE); + + auto data = make_unique>(aTrainData[0].size(), configurations.GetDimension()); + auto *train_locations = new dataunits::Locations(aTrainData[0].size(), configurations.GetDimension()); + auto *test_locations = new dataunits::Locations(aTestData[0].size(), configurations.GetDimension()); + + auto* z_values = new double[aTrainData.back().size()]; + for(int i = 0; i< aTrainData[0].size(); i++){ + train_locations->SetLocationX(*aTrainData[0].data(), aTrainData[0].size()); + train_locations->SetLocationY(*aTrainData[1].data(), aTrainData[1].size()); + if(aTrainData.size() > 3) { + train_locations->SetLocationZ(*aTrainData[2].data(), aTrainData[2].size()); } + } + data->SetLocations(*train_locations); + memcpy(z_values, aTrainData.back().data(), aTrainData.back().size() * sizeof(double)); + for(int i = 0; i< aTestData[0].size(); i++){ + test_locations->SetLocationX(*aTestData[0].data(), aTestData[0].size()); + test_locations->SetLocationY(*aTestData[1].data(), aTestData[1].size()); + } - if (aLocationsZ == nullptr || aLocationsZ.isNotNull()) { - double *pLocationsZPtr; - NumericVector mat_location(aLocationsZ.get()); - pLocationsZPtr = &mat_location[0]; // Get the raw pointer to the matrix data - apData->GetLocations()->SetLocationZ(*pLocationsZPtr, mat_location.size()); + // Set common configurations. + configurations.CheckKernelValue(aKernelName); + configurations.ParseDistanceMetric(aDistanceMatrix); + configurations.SetDenseTileSize(aDenseTileSize); + configurations.SetLowTileSize(aLowTileSize); + configurations.SetDimension(Configurations::CheckDimensionValue(aDimension)); + configurations.SetProblemSize(data->GetLocations()->GetSize()); + configurations.SetEstimatedTheta(aEstimatedTheta); + configurations.SetInitialTheta(aTrueTheta); + configurations.SetIsMLOEMMOM(TRUE); + + ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_values, train_locations, test_locations); + + // Take back ownership, to avoid deleting apData when the unique_ptr goes out of scope. + auto temp_data = data.release(); + delete[] z_values; + + vector mloe_mmom_values; + mloe_mmom_values.push_back(Results::GetInstance()->GetMLOE()); + mloe_mmom_values.push_back(Results::GetInstance()->GetMMOM()); + return mloe_mmom_values; + } + + + vector + R_ExaGeoStatFisher(const string &aKernelName, const string &aDistanceMatrix, const vector &aEstimatedTheta, + const int &aDenseTileSize, const int &aLowTileSize, const string &aDimension, + vector> &aTrainData, + vector> &aTestData) { + + Configurations configurations; + configurations.SetComputation(EXACT_DENSE); + + auto data = make_unique>(aTrainData[0].size(), configurations.GetDimension()); + auto *train_locations = new dataunits::Locations(aTrainData[0].size(), configurations.GetDimension()); + auto *test_locations = new dataunits::Locations(aTestData[0].size(), configurations.GetDimension()); + + auto* z_values = new double[aTrainData.back().size()]; + for(int i = 0; i< aTrainData[0].size(); i++){ + train_locations->SetLocationX(*aTrainData[0].data(), aTrainData[0].size()); + train_locations->SetLocationY(*aTrainData[1].data(), aTrainData[1].size()); + if(aTrainData.size() > 3) { + train_locations->SetLocationZ(*aTrainData[2].data(), aTrainData[2].size()); } } + data->SetLocations(*train_locations); + memcpy(z_values, aTrainData.back().data(), aTrainData.back().size() * sizeof(double)); + for(int i = 0; i< aTestData[0].size(); i++){ + test_locations->SetLocationX(*aTestData[0].data(), aTestData[0].size()); + test_locations->SetLocationY(*aTestData[1].data(), aTestData[1].size()); + } - exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(*apHardware, *apConfigurations, apDataPtr, pMeasurementsVectorPtr); - return apDataPtr.release(); + // Set common configurations. + configurations.CheckKernelValue(aKernelName); + configurations.ParseDistanceMetric(aDistanceMatrix); + configurations.SetProblemSize(data->GetLocations()->GetSize()); + configurations.SetDenseTileSize(aDenseTileSize); + configurations.SetLowTileSize(aLowTileSize); + configurations.SetEstimatedTheta(aEstimatedTheta); + configurations.SetIsFisher(TRUE); + + ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_values, train_locations, test_locations); + // Take back ownership, to avoid deleting apData when the unique_ptr goes out of scope. + auto temp_data = data.release(); + delete[] z_values; + return Results::GetInstance()->GetFisherMatrix(); } - ExaGeoStatData *R_ExaGeoStatPredictData(ExaGeoStatHardware *apHardware, Configurations *apConfigurations, - ExaGeoStatData *apData, - Nullable aMeasurementsVector, - Nullable aLocationsX, - Nullable aLocationsY, - Nullable aLocationsZ) { + vector R_ExaGeoStatIDW(const string &aKernelName, const string &aDistanceMatrix, + const vector &aEstimatedTheta, + const int &aDenseTileSize, const int &aLowTileSize, + const string &aDimension, + vector> &aTrainData, + vector> &aTestData, vector &aTestMeasurementsValues) { - std::unique_ptr> apDataPtr(apData); - double* pMeasurementsVectorPtr = nullptr; - if (aMeasurementsVector == nullptr || aMeasurementsVector.isNull()){ - // This was the only way to pass C++ tests, if we checked for aMeasurementsVector != nullptr a seg fault occurs + Configurations configurations; + configurations.SetComputation(EXACT_DENSE); + + auto data = make_unique>(aTrainData[0].size(), configurations.GetDimension()); + auto *train_locations = new dataunits::Locations(aTrainData[0].size(), configurations.GetDimension()); + auto *test_locations = new dataunits::Locations(aTestData[0].size(), configurations.GetDimension()); + + // Allocate memory for z_values to hold elements from both sources + auto* z_values = new double[aTrainData.back().size() + aTestMeasurementsValues.size()]; + + for(int i = 0; i< aTrainData[0].size(); i++){ + train_locations->SetLocationX(*aTrainData[0].data(), aTrainData[0].size()); + train_locations->SetLocationY(*aTrainData[1].data(), aTrainData[1].size()); + if(aTrainData.size() > 3) { + train_locations->SetLocationZ(*aTrainData[2].data(), aTrainData[2].size()); + } + } + // Copy data from the last element of aTrainData to the beginning of z_values + memcpy(z_values, aTrainData.back().data(), aTrainData.back().size() * sizeof(double)); + // Calculate the starting position for the next part of the data in z_values + auto* destination = z_values + aTrainData.back().size(); + // Copy data from aTestMeasurementsValues to the next part of z_values, after the previously copied data + memcpy(destination, aTestMeasurementsValues.data(), aTestMeasurementsValues.size() * sizeof(double)); + + for(int i = 0; i< aTestData[0].size(); i++){ + test_locations->SetLocationX(*aTestData[0].data(), aTestData[0].size()); + test_locations->SetLocationY(*aTestData[1].data(), aTestData[1].size()); } - else{ + // Set common configurations. + configurations.CheckKernelValue(aKernelName); + configurations.ParseDistanceMetric(aDistanceMatrix); + configurations.SetDenseTileSize(aDenseTileSize); + configurations.SetLowTileSize(aLowTileSize); + configurations.SetDimension(Configurations::CheckDimensionValue(aDimension)); + configurations.SetProblemSize(data->GetLocations()->GetSize()); + configurations.SetEstimatedTheta(aEstimatedTheta); + configurations.SetIsIDW(TRUE); + + ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_values, train_locations, test_locations); + // Take back ownership, to avoid deleting apData when the unique_ptr goes out of scope. + auto temp_data = data.release(); + delete[] z_values; + + return Results::GetInstance()->GetIDWError(); + } + + double *GetDataFromArguments(Nullable aMeasurementsVector, Nullable aLocationsX, + Nullable aLocationsY, Nullable aLocationsZ, + unique_ptr > &aData, Configurations &aConfigurations, + const string &aKernelName, const string &aDistanceMatrix, const int &aDenseTileSize, + const int &aLowTileSize, + const string &aDimension, const Computation &aComputation) { + + double *pMeasurementsVectorPtr = nullptr; + if (aMeasurementsVector == nullptr || aMeasurementsVector.isNull()) { + // This was the only way to pass C++ tests, if we checked for aMeasurementsVector != nullptr a seg fault occurs + } else { NumericVector mat(aMeasurementsVector.get()); pMeasurementsVectorPtr = &mat[0]; // Get the raw pointer to the matrix data @@ -308,26 +381,55 @@ namespace exageostat::adapters { double *pLocationsXPtr; NumericVector mat_location(aLocationsX.get()); pLocationsXPtr = &mat_location[0]; // Get the raw pointer to the matrix data - apData->GetLocations()->SetLocationX(*pLocationsXPtr, mat_location.size()); + // Check for overflow + if (mat_location.size() > numeric_limits::max()) { + // Handle the overflow by logging an error + throw runtime_error("Warning: Size exceeds the limit of int type for Locations X."); + } else { + // Safe to convert + int size = static_cast(mat_location.size()); + aData->GetLocations()->SetLocationX(*pLocationsXPtr, size); + } } if (aLocationsY.isNotNull()) { double *pLocationsYPtr; NumericVector mat_location(aLocationsY.get()); pLocationsYPtr = &mat_location[0]; // Get the raw pointer to the matrix data - apData->GetLocations()->SetLocationY(*pLocationsYPtr, mat_location.size()); - + // Check for overflow + if (mat_location.size() > numeric_limits::max()) { + // Handle the overflow by logging an error + throw runtime_error("Warning: Size exceeds the limit of int type for Locations Y."); + } else { + // Safe to convert + int size = static_cast(mat_location.size()); + aData->GetLocations()->SetLocationY(*pLocationsYPtr, size); + } } if (aLocationsZ.isNotNull()) { double *pLocationsZPtr; NumericVector mat_location(aLocationsZ.get()); pLocationsZPtr = &mat_location[0]; // Get the raw pointer to the matrix data - apData->GetLocations()->SetLocationZ(*pLocationsZPtr, mat_location.size()); + // Check for overflow + if (mat_location.size() > numeric_limits::max()) { + // Handle the overflow by logging an error + throw runtime_error("Warning: Size exceeds the limit of int type for Locations Z."); + } else { + // Safe to convert + int size = static_cast(mat_location.size()); + aData->GetLocations()->SetLocationZ(*pLocationsZPtr, size); + } } } - - exageostat::api::ExaGeoStat::ExaGeoStatPrediction(*apHardware, *apConfigurations, apDataPtr, pMeasurementsVectorPtr); - return apDataPtr.release(); + // Set common configurations. + aConfigurations.SetComputation(aComputation); + aConfigurations.CheckKernelValue(aKernelName); + aConfigurations.ParseDistanceMetric(aDistanceMatrix); + aConfigurations.SetDenseTileSize(aDenseTileSize); + aConfigurations.SetLowTileSize(aLowTileSize); + aConfigurations.SetDimension(Configurations::CheckDimensionValue(aDimension)); + aConfigurations.SetProblemSize(aData->GetLocations()->GetSize()); + return pMeasurementsVectorPtr; } } \ No newline at end of file diff --git a/src/Rcpp-adapters/RcppModules.cpp b/src/Rcpp-adapters/RcppModules.cpp index efc6ea69..60ed9691 100644 --- a/src/Rcpp-adapters/RcppModules.cpp +++ b/src/Rcpp-adapters/RcppModules.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -23,7 +22,6 @@ * Allows C++ to Send and Receive Class object from R **/ RCPP_EXPOSED_CLASS(ExaGeoStatHardware) -RCPP_EXPOSED_CLASS(Configurations) RCPP_EXPOSED_CLASS_NODECL(ExaGeoStatData) /** Expose C++ Object With the Given functions **/ @@ -37,32 +35,31 @@ RCPP_MODULE(ExaGeoStatCPP) { .constructor() .method("finalize_hardware", &ExaGeoStatHardware::FinalizeHardware, "Manually finalize the hardware"); - /** Configurations Class **/ - class_("Configurations") - .constructor(); - /** Data Class **/ class_>("Data") .constructor(); - /** Configurations Function **/ - function("configurations_init", &exageostat::adapters::R_InitializeArguments, - List::create(_["n"], _["kernel"], _["tile_size"], _["p_q"] = IntegerVector::create(1, 1), - _["time_slot"] = 1, _["computation"] = "exact", _["precision"] = "double", - _["cores_gpus"] = IntegerVector::create(1, 0), _["band"] = 1, _["max_rank"] = 500, - _["iTheta"], _["lb_ub"], _["eTheta"] = IntegerVector::create(-1, -1, -1), - _["verbose"] = "standard", _["dimension"] = "2D", _["mle_itr"] = 0, _["tol"] = 4, - _["prediction"] = IntegerVector::create(0, 0, 0, 0, 0), - _["paths"] = StringVector::create("", "", "", ""), _["save_data"] = 0)); - function("simulate_data", &exageostat::adapters::R_ExaGeoStatLoadData, - List::create(_["hardware"], _["config"], _["data"])); + List::create(_["kernel"], _["initial_theta"], _["distance_matrix"] = "euclidean", _["problem_size"], + _["seed"] = 0, _["dts"], _["lts"] = 0, _["dimension"] = "2D", _["log_path"] = "", + _["data_path"] = "", _["observations_file"] = "", _["recovery_file"] = "")); + function("model_data", &exageostat::adapters::R_ExaGeoStatModelData, - List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, - _["y"] = R_NilValue, _["z"] = R_NilValue)); + List::create(_["computation"] = "exact", _["kernel"], _["distance_matrix"] = "euclidean", _["lb"], _["ub"], + _["tol"] = 4, _["mle_itr"], _["dts"], _["lts"] = 0, _["dimension"] = "2D",_["band"] = 0,_["max_rank"] = 500, _["data"] = R_NilValue, + _["matrix"] = R_NilValue, _["x"] = R_NilValue, _["y"] = R_NilValue, _["z"] = R_NilValue)); + function("predict_data", &exageostat::adapters::R_ExaGeoStatPredictData, - List::create(_["hardware"], _["config"], _["data"], _["matrix"] = R_NilValue, _["x"] = R_NilValue, - _["y"] = R_NilValue, _["z"] = R_NilValue)); + List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["dts"], _["lts"] = 0, _["dimension"] = "2D",_["train_data"], _["test_data"])); + + function("mloe_mmom", &exageostat::adapters::R_ExaGeoStatMLOE_MMOM, + List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["true_theta"], _["dts"], _["lts"] = 0, _["dimension"] = "2D", _["train_data"], _["test_data"])); + + function("fisher", &exageostat::adapters::R_ExaGeoStatFisher, + List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["dts"], _["lts"] = 0, _["dimension"] = "2D", _["train_data"], _["test_data"])); + + function("idw", &exageostat::adapters::R_ExaGeoStatIDW, + List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["dts"], _["lts"] = 0, _["dimension"] = "2D", _["train_data"], _["test_data"], _["test_measurements"])); function("get_locationsX", &exageostat::adapters::R_GetLocationX, List::create(_["data"])); function("get_locationsY", &exageostat::adapters::R_GetLocationY, List::create(_["data"])); diff --git a/src/api/ExaGeoStat.cpp b/src/api/ExaGeoStat.cpp index 6106a4ff..4a2d34c9 100644 --- a/src/api/ExaGeoStat.cpp +++ b/src/api/ExaGeoStat.cpp @@ -23,13 +23,12 @@ using namespace exageostat::api; using namespace exageostat::generators; using namespace exageostat::dataunits; using namespace exageostat::prediction; +using namespace exageostat::configurations; template -void ExaGeoStat::ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, - Configurations &aConfigurations, - std::unique_ptr> &aData) { +void ExaGeoStat::ExaGeoStatLoadData(Configurations &aConfigurations, std::unique_ptr> &aData) { - LOGGER("** ExaGeoStat data generation **") + LOGGER("** ExaGeoStat data generation/loading **") // Register and create a kernel object kernels::Kernel *pKernel = plugins::PluginRegistry>::Create(aConfigurations.GetKernelName(), aConfigurations.GetTimeSlot()); @@ -37,13 +36,14 @@ void ExaGeoStat::ExaGeoStatLoadData(const ExaGeoStatHardware &aHardware, aConfigurations.InitializeDataGenerationArguments(); // Create a unique pointer to a DataGenerator object unique_ptr> data_generator = DataGenerator::CreateGenerator(aConfigurations); - aData = data_generator->CreateData(aConfigurations, aHardware, *pKernel); + aData = data_generator->CreateData(aConfigurations, *pKernel); delete pKernel; + LOGGER("\t*Data generation/loading finished*") } template -T ExaGeoStat::ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - std::unique_ptr> &aData, T *apMeasurementsMatrix) { +T ExaGeoStat::ExaGeoStatDataModeling(Configurations &aConfigurations, std::unique_ptr> &aData, + T *apMeasurementsMatrix) { LOGGER("** ExaGeoStat data Modeling **") // Register and create a kernel object @@ -55,14 +55,14 @@ T ExaGeoStat::ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, Con int parameters_number = pKernel->GetParametersNumbers(); int max_number_of_iterations = aConfigurations.GetMaxMleIterations(); // Setting struct of data to pass to the modeling. - auto modeling_data = new mModelingData(aData, aConfigurations, aHardware, *apMeasurementsMatrix, *pKernel); + auto modeling_data = new mModelingData(aData, aConfigurations, *apMeasurementsMatrix, *pKernel); // Create nlopt double opt_f; opt optimizing_function(nlopt::LN_BOBYQA, parameters_number); // Initialize problem's bound. optimizing_function.set_lower_bounds(aConfigurations.GetLowerBounds()); optimizing_function.set_upper_bounds(aConfigurations.GetUpperBounds()); - optimizing_function.set_ftol_abs(pow(10, -1 * aConfigurations.GetTolerance())); + optimizing_function.set_ftol_abs(aConfigurations.GetTolerance()); // Set max iterations value. optimizing_function.set_maxeval(max_number_of_iterations); optimizing_function.set_max_objective(ExaGeoStatMLETileAPI, (void *) modeling_data); @@ -78,23 +78,23 @@ T ExaGeoStat::ExaGeoStatDataModeling(const ExaGeoStatHardware &aHardware, Con template double ExaGeoStat::ExaGeoStatMLETileAPI(const std::vector &aTheta, std::vector &aGrad, void *apInfo) { + auto config = ((mModelingData *) apInfo)->mpConfiguration; auto data = ((mModelingData *) apInfo)->mpData; - auto hardware = ((mModelingData *) apInfo)->mpHardware; auto measurements = ((mModelingData *) apInfo)->mpMeasurementsMatrix; auto kernel = ((mModelingData *) apInfo)->mpKernel; // We do Date Modeling with any computation. auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver( config->GetComputation()); - return linear_algebra_solver->ExaGeoStatMLETile(*hardware, *data, *config, aTheta.data(), measurements, *kernel); + return linear_algebra_solver->ExaGeoStatMLETile(*data, *config, aTheta.data(), measurements, *kernel); } template -void ExaGeoStat::ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, Configurations &aConfigurations, - std::unique_ptr> &aData, - T *apMeasurementsMatrix) { +void ExaGeoStat::ExaGeoStatPrediction(Configurations &aConfigurations, std::unique_ptr> &aData, + T *apMeasurementsMatrix, Locations *apTrainLocations, + Locations *apTestLocations) { LOGGER("** ExaGeoStat data Prediction **") // Register and create a kernel object @@ -102,6 +102,7 @@ void ExaGeoStat::ExaGeoStatPrediction(const ExaGeoStatHardware &aHardware, Co aConfigurations.GetTimeSlot()); // Add the data prediction arguments. aConfigurations.InitializeDataPredictionArguments(); - Prediction::PredictMissingData(aHardware, aData, aConfigurations, apMeasurementsMatrix, *pKernel); + Prediction::PredictMissingData(aData, aConfigurations, apMeasurementsMatrix, *pKernel, apTrainLocations, + apTestLocations); delete pKernel; } \ No newline at end of file diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index b8bccee7..e89844da 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -22,6 +22,7 @@ using namespace std; +using namespace exageostat::configurations; using namespace exageostat::common; Verbose Configurations::mVerbosity = Verbose::STANDARD_MODE; @@ -230,7 +231,7 @@ void Configurations::InitializeAllTheta() { if (!mIsThetaInit) { - int parameters_number = exageostat::kernels::KernelsConfigurations::GetParametersNumberKernelMap()[this->GetKernelName()]; + int parameters_number = kernels::KernelsConfigurations::GetParametersNumberKernelMap()[this->GetKernelName()]; InitTheta(GetInitialTheta(), parameters_number); SetInitialTheta(GetInitialTheta()); @@ -609,9 +610,9 @@ int Configurations::CheckUnknownObservationsValue(const string &aValue) { } void Configurations::ParseDistanceMetric(const std::string &aDistanceMetric) { - if (aDistanceMetric == "eg" || aDistanceMetric == "EG") { + if (aDistanceMetric == "eg" || aDistanceMetric == "EG" || aDistanceMetric == "euclidean") { SetDistanceMetric(EUCLIDEAN_DISTANCE); - } else if (aDistanceMetric == "gcd" || aDistanceMetric == "GCD") { + } else if (aDistanceMetric == "gcd" || aDistanceMetric == "GCD"|| aDistanceMetric == "great_circle") { SetDistanceMetric(GREAT_CIRCLE_DISTANCE); } else { throw range_error("Invalid value. Please use eg or gcd values only."); @@ -643,9 +644,9 @@ void Configurations::PrintSummary(int aRank) { LOGGER("********************SUMMARY**********************") if (this->GetIsSynthetic()) { - LOGGER("#Synthetic Dataset") + LOGGER("#Synthetic Data generation") } else { - LOGGER("#Real Dataset") + LOGGER("#Real Data loader") } LOGGER("#Number of Locations: " << this->GetProblemSize()) LOGGER("#Threads per node: " << this->GetCoresNumber()) @@ -708,3 +709,7 @@ Configurations::~Configurations() { } this->mpArgV = nullptr; } + +void Configurations::SetTolerance(double aTolerance) { + mDictionary["Tolerance"] = pow(10, -1 * aTolerance); +} diff --git a/src/data-generators/DataGenerator.cpp b/src/data-generators/DataGenerator.cpp index 342a95b5..892d0fdd 100644 --- a/src/data-generators/DataGenerator.cpp +++ b/src/data-generators/DataGenerator.cpp @@ -21,9 +21,10 @@ using namespace exageostat::dataLoader::csv; using namespace exageostat::generators::synthetic; using namespace exageostat::dataunits; using namespace exageostat::common; +using namespace exageostat::results; template -std::unique_ptr> DataGenerator::CreateGenerator(Configurations &apConfigurations) { +std::unique_ptr> DataGenerator::CreateGenerator(configurations::Configurations &apConfigurations) { //// TODO: In case of other file support, Then we can create another layer for the factory creation depending on the file size. // Check the used Data generation method, whether it's synthetic or real. @@ -31,10 +32,10 @@ std::unique_ptr> DataGenerator::CreateGenerator(Configuratio // Return DataGenerator unique pointer of Synthetic type if (aDataSourceType == SYNTHETIC) { - results::Results::GetInstance()->SetIsSynthetic(true); + Results::GetInstance()->SetIsSynthetic(true); return std::unique_ptr>(SyntheticGenerator::GetInstance()); } else if (aDataSourceType == CSV_FILE) { - results::Results::GetInstance()->SetIsSynthetic(false); + Results::GetInstance()->SetIsSynthetic(false); return std::unique_ptr>(CSVLoader::GetInstance()); } else { throw std::runtime_error("Data Loading for this file type is unsupported for now"); diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index 39708359..e113b0a0 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -14,10 +14,13 @@ #include #include +#include using namespace exageostat::generators::synthetic; using namespace exageostat::dataunits; using namespace exageostat::common; +using namespace exageostat::configurations; +using namespace exageostat::results; template SyntheticGenerator *SyntheticGenerator::GetInstance() { @@ -31,7 +34,6 @@ SyntheticGenerator *SyntheticGenerator::GetInstance() { template std::unique_ptr> SyntheticGenerator::CreateData(Configurations &aConfigurations, - const ExaGeoStatHardware &aHardware, exageostat::kernels::Kernel &aKernel) { int n = aConfigurations.GetProblemSize() * aConfigurations.GetTimeSlot(); @@ -51,18 +53,18 @@ SyntheticGenerator::CreateData(Configurations &aConfigurations, // Generate Descriptors phase auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); - linear_algebra_solver->GenerateSyntheticData(aConfigurations, aHardware, data, aKernel); + linear_algebra_solver->GenerateSyntheticData(aConfigurations, data, aKernel); if (aConfigurations.GetLogger()) { VERBOSE("Writing generated data to the disk (Synthetic Dataset Generation Phase) .....") -#ifdef CHAMELEON_USE_MPI +#ifdef USE_MPI auto pMatrix = new T[aConfigurations.GetProblemSize()]; std::string path = aConfigurations.GetLoggerPath(); CHAMELEON_Desc2Lap(ChamUpperLower, data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc, pMatrix, aConfigurations.GetProblemSize()); if (helpers::CommunicatorMPI::GetInstance()->GetRank() == 0){ - helpers::DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + dataLoader::csv::CSVLoader::GetInstance()->WriteData(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), aConfigurations.GetProblemSize(), parameters_number, path, *data->GetLocations()); @@ -70,16 +72,16 @@ SyntheticGenerator::CreateData(Configurations &aConfigurations, delete[] pMatrix; #else std::string path = aConfigurations.GetLoggerPath(); - helpers::DiskWriter::WriteVectorsToDisk(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + dataLoader::csv::CSVLoader::GetInstance()->WriteData(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc->mat), aConfigurations.GetProblemSize(), aKernel.GetVariablesNumber(), path, *data->GetLocations()); #endif VERBOSE("Done.") } - results::Results::GetInstance()->SetGeneratedLocationsNumber(n); - results::Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); - results::Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); + Results::GetInstance()->SetGeneratedLocationsNumber(n); + Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); + Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); return data; } diff --git a/src/data-loader/CMakeLists.txt b/src/data-loader/CMakeLists.txt index 1c5d2e4c..9338b55c 100644 --- a/src/data-loader/CMakeLists.txt +++ b/src/data-loader/CMakeLists.txt @@ -5,7 +5,7 @@ # @file CMakeLists.txt # @brief CMake configuration file for Data loader module -# @version 1.0.0 +# @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-02-14 diff --git a/src/data-loader/DataLoader.cpp b/src/data-loader/DataLoader.cpp index d4da658f..d1dc51f4 100644 --- a/src/data-loader/DataLoader.cpp +++ b/src/data-loader/DataLoader.cpp @@ -13,18 +13,18 @@ **/ #include +#include using namespace std; using namespace exageostat::dataLoader; using namespace exageostat::dataunits; using namespace exageostat::common; +using namespace exageostat::results; template std::unique_ptr> -DataLoader::CreateData(Configurations &aConfigurations, - const ExaGeoStatHardware &aHardware, - exageostat::kernels::Kernel &aKernel) { +DataLoader::CreateData(configurations::Configurations &aConfigurations, exageostat::kernels::Kernel &aKernel) { // create vectors that will be populated with read data. vector measurements_vector; @@ -44,7 +44,6 @@ DataLoader::CreateData(Configurations &aConfigurations, //Initialize the descriptors. auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); - linear_algebra_solver->SetContext(aHardware.GetChameleonContext()); linear_algebra_solver->InitiateDescriptors(aConfigurations, *data->GetDescriptorData(), p); linear_algebra_solver->ExaGeoStatLaSetTile(EXAGEOSTAT_UPPER_LOWER, 0, 0, data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, @@ -62,9 +61,9 @@ DataLoader::CreateData(Configurations &aConfigurations, DESCRIPTOR_Z).chameleon_desc->mat)[i] = measurements_vector[i]; } - results::Results::GetInstance()->SetGeneratedLocationsNumber(aConfigurations.GetProblemSize() / p); - results::Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); - results::Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); + Results::GetInstance()->SetGeneratedLocationsNumber(aConfigurations.GetProblemSize() / p); + Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); + Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); return data; } diff --git a/src/data-loader/concrete/CSVLoader.cpp b/src/data-loader/concrete/CSVLoader.cpp index a255f642..661d7c53 100644 --- a/src/data-loader/concrete/CSVLoader.cpp +++ b/src/data-loader/concrete/CSVLoader.cpp @@ -18,6 +18,7 @@ using namespace std; +using namespace exageostat::configurations; using namespace exageostat::dataunits; using namespace exageostat::kernels; using namespace exageostat::common; @@ -35,7 +36,7 @@ CSVLoader *CSVLoader::GetInstance() { template void CSVLoader::ReadData(Configurations &aConfigurations, vector &aMeasurementsMatrix, vector &aXLocations, - vector &aYLocations, vector &aZLocations, const int &aP) { + vector &aYLocations, vector &aZLocations, const int &aP) { //Check if the user entered a valid path for the CSV file. if (aConfigurations.GetDataPath().empty()) { @@ -138,7 +139,99 @@ void CSVLoader::ReadData(Configurations &aConfigurations, vector &aMeasure aConfigurations.SetProblemSize(index * aP / aConfigurations.GetTimeSlot()); file.close(); - LOGGER("Data is read from " << data_path << " successfully.") + LOGGER("\tData is read from " << data_path << " successfully.") +} + +template +void CSVLoader::WriteData(const T &aMatrixPointer, const int &aProblemSize, const int &aP, std::string &aLoggerPath, + dataunits::Locations &aLocations) { + // Determine the path for storing the output files + if (aLoggerPath.empty()) { + aLoggerPath = LOG_PATH; + } else { + if (aLoggerPath.back() == '/') { + aLoggerPath += "synthetic_ds"; + } else { + aLoggerPath += "/synthetic_ds"; + } + } + // Create a new directory if it does not already exist + bool created; + if (!filesystem::exists(aLoggerPath)) { + try { + created = filesystem::create_directories(aLoggerPath); + } catch (const filesystem::filesystem_error &e) { + throw runtime_error("Error creating directory: " + aLoggerPath); + } + } else { + created = true; + } + + // Check if the directory was created successfully + if (!created) { + throw runtime_error("Error creating directory: " + aLoggerPath); + } + + // Determine the names of the output files + size_t i = 1, j; + std::ofstream p_file_synthetic, p_file_log; + std::string n_file_synthetic = aLoggerPath + "/SYN_" + std::to_string(aProblemSize / aP) + "_"; + std::string n_file_log = aLoggerPath + "/log_" + std::to_string(aProblemSize / aP) + "_"; + std::string temp = n_file_log + std::to_string(i); + + // Check if log file exists + while (std::filesystem::exists(temp)) { + i++; + temp = n_file_log + std::to_string(i); + } + + n_file_synthetic += std::to_string(i); + p_file_synthetic.open(n_file_synthetic); + + for (j = 0, i = 0; i < aProblemSize / aP; i++) { + if (aLocations.GetLocationZ() == nullptr) { + //2 Dimensions + if (aP == 1) { + p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' + << aLocations.GetLocationY()[i] << "," << std::setprecision(15) << (&aMatrixPointer)[i] + << '\n'; + } else if (aP == 2) { + p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' + << aLocations.GetLocationY()[i] << "," << std::setprecision(15) << (&aMatrixPointer)[j] + << "," + << std::setprecision(15) << (&aMatrixPointer)[j + 1] << '\n'; + j += 2; + } else if (aP == 3) { + p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' + << aLocations.GetLocationY()[i] << "," << std::setprecision(15) << (&aMatrixPointer)[j] + << "," + << std::setprecision(15) << (&aMatrixPointer)[j + 1] << "," << std::setprecision(15) + << (&aMatrixPointer)[j + 2] << '\n'; + j += 3; + } + } else { + //3 Dimensions + if (aP == 1) { + p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' + << aLocations.GetLocationY()[i] << ',' << aLocations.GetLocationZ()[i] << "," + << std::setprecision(15) << (&aMatrixPointer)[i] << '\n'; + } else if (aP == 2) { + p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' + << aLocations.GetLocationY()[i] << ',' << aLocations.GetLocationZ()[i] << "," + << std::setprecision(15) << (&aMatrixPointer)[j] << std::setprecision(15) << "," + << (&aMatrixPointer)[j + 1] << '\n'; + j += 2; + } else if (aP == 3) { + p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' + << aLocations.GetLocationY()[i] << ',' << aLocations.GetLocationZ()[i] << "," + << std::setprecision(15) << (&aMatrixPointer)[j] << "," << std::setprecision(15) + << (&aMatrixPointer)[j + 1] << "," << std::setprecision(15) << (&aMatrixPointer)[j + 2] + << '\n'; + j += 3; + } + } + } + p_file_synthetic.close(); } template diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 7b17a727..6496900a 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -21,6 +21,7 @@ #include using namespace exageostat::common; +using namespace exageostat::results; ExaGeoStatHardware::ExaGeoStatHardware(const Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber) { @@ -77,7 +78,7 @@ void ExaGeoStatHardware::FinalizeHardware(){ ExaGeoStatHardware::~ExaGeoStatHardware() { - exageostat::results::Results::GetInstance()->PrintEndSummary(); + Results::GetInstance()->PrintEndSummary(); // finalize hardware using Chameleon if (mpChameleonContext) { CHAMELEON_Finalize() diff --git a/src/helpers/CMakeLists.txt b/src/helpers/CMakeLists.txt index 550bc47d..06a01857 100644 --- a/src/helpers/CMakeLists.txt +++ b/src/helpers/CMakeLists.txt @@ -10,7 +10,6 @@ # @date 2023-06-08 set(SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/DiskWriter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DistanceCalculationHelpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CommunicatorMPI.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ByteHandler.cpp diff --git a/src/helpers/DiskWriter.cpp b/src/helpers/DiskWriter.cpp deleted file mode 100644 index 3cef39cf..00000000 --- a/src/helpers/DiskWriter.cpp +++ /dev/null @@ -1,113 +0,0 @@ - -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -// All rights reserved. -// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -/** - * @file DiskWriter.cpp - * @brief Contains the implementation of the DiskWriter class for writing data to disk. - * @version 1.1.0 - * @author Mahmoud ElKarargy - * @author Sameh Abdulah - * @date 2023-06-08 -**/ - -#include - -#include - -using namespace std; - -using namespace exageostat::helpers; -using namespace exageostat::dataunits; - -template -void DiskWriter::WriteVectorsToDisk(const T &aMatrixPointer, const int &aProblemSize, const int &aP, - std::string &aLoggerPath, Locations &aLocations) { - - // Determine the path for storing the output files - if (aLoggerPath.empty()) { - aLoggerPath = LOG_PATH; - } else { - if (aLoggerPath.back() == '/') { - aLoggerPath += "synthetic_ds"; - } else { - aLoggerPath += "/synthetic_ds"; - } - } - // Create a new directory if it does not already exist - bool created; - if (!filesystem::exists(aLoggerPath)) { - try { - created = filesystem::create_directories(aLoggerPath); - } catch (const filesystem::filesystem_error &e) { - throw runtime_error("Error creating directory: " + aLoggerPath); - } - } else { - created = true; - } - - // Check if the directory was created successfully - if (!created) { - throw runtime_error("Error creating directory: " + aLoggerPath); - } - - // Determine the names of the output files - size_t i = 1, j; - std::ofstream p_file_synthetic, p_file_log; - std::string n_file_synthetic = aLoggerPath + "/SYN_" + std::to_string(aProblemSize / aP) + "_"; - std::string n_file_log = aLoggerPath + "/log_" + std::to_string(aProblemSize / aP) + "_"; - std::string temp = n_file_log + std::to_string(i); - - // Check if log file exists - while (std::filesystem::exists(temp)) { - i++; - temp = n_file_log + std::to_string(i); - } - - n_file_synthetic += std::to_string(i); - p_file_synthetic.open(n_file_synthetic); - - for (j = 0, i = 0; i < aProblemSize / aP; i++) { - if (aLocations.GetLocationZ() == nullptr) { - //2 Dimensions - if (aP == 1) { - p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' - << aLocations.GetLocationY()[i] << "," << std::setprecision(15) << (&aMatrixPointer)[i] - << '\n'; - } else if (aP == 2) { - p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' - << aLocations.GetLocationY()[i] << "," << std::setprecision(15) << (&aMatrixPointer)[j] << "," - << std::setprecision(15) << (&aMatrixPointer)[j + 1] << '\n'; - j += 2; - } else if (aP == 3) { - p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' - << aLocations.GetLocationY()[i] << "," << std::setprecision(15) << (&aMatrixPointer)[j] << "," - << std::setprecision(15) << (&aMatrixPointer)[j + 1] << "," << std::setprecision(15) - << (&aMatrixPointer)[j + 2] << '\n'; - j += 3; - } - } else { - //3 Dimensions - if (aP == 1) { - p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' - << aLocations.GetLocationY()[i] << ',' << aLocations.GetLocationZ()[i] << "," - << std::setprecision(15) << (&aMatrixPointer)[i] << '\n'; - } else if (aP == 2) { - p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' - << aLocations.GetLocationY()[i] << ',' << aLocations.GetLocationZ()[i] << "," - << std::setprecision(15) << (&aMatrixPointer)[j] << std::setprecision(15) << "," - << (&aMatrixPointer)[j + 1] << '\n'; - j += 2; - } else if (aP == 3) { - p_file_synthetic << std::setprecision(15) << aLocations.GetLocationX()[i] << ',' - << aLocations.GetLocationY()[i] << ',' << aLocations.GetLocationZ()[i] << "," - << std::setprecision(15) << (&aMatrixPointer)[j] << "," << std::setprecision(15) - << (&aMatrixPointer)[j + 1] << "," << std::setprecision(15) << (&aMatrixPointer)[j + 2] - << '\n'; - j += 3; - } - } - } - p_file_synthetic.close(); -} \ No newline at end of file diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index d707fbb9..335167a0 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -19,12 +19,16 @@ #include #include +#include using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; using namespace exageostat::dataunits; +using namespace exageostat::configurations; +using namespace exageostat::runtime; +using namespace exageostat::results; // Define a method to set up the Chameleon descriptors template @@ -32,7 +36,7 @@ void LinearAlgebraMethods::InitiateDescriptors(Configurations &aConfiguration const int &aP, T *apMeasurementsMatrix) { // Check for initialize the Chameleon context. - if (!this->mpContext) { + if (!ExaGeoStatHardware::GetChameleonContext()) { throw std::runtime_error( "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); } @@ -116,7 +120,7 @@ void LinearAlgebraMethods::InitiateFisherDescriptors(Configurations &aConfigu dataunits::DescriptorData &aDescriptorData) { // Check for initialize the Chameleon context. - if (!this->mpContext) { + if (!ExaGeoStatHardware::GetChameleonContext()) { throw std::runtime_error( "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); } @@ -178,10 +182,9 @@ void LinearAlgebraMethods::InitiateFisherDescriptors(Configurations &aConfigu } template -void LinearAlgebraMethods::InitiatePredictionDescriptors( - Configurations &aConfigurations, std::unique_ptr> &aData, const int &aP) { +void LinearAlgebraMethods::InitiatePredictionDescriptors(Configurations &aConfigurations, std::unique_ptr> &aData) { - if (!this->mpContext) { + if (!ExaGeoStatHardware::GetChameleonContext()) { throw std::runtime_error( "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); } @@ -254,12 +257,12 @@ void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfi std::unique_ptr> &aData, const int &aP) { - if (!this->mpContext) { + if (!ExaGeoStatHardware::GetChameleonContext()) { throw std::runtime_error( "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); } - int n_z_obs = aConfigurations.CalculateZObsNumber(); + int n_z_obs = aConfigurations.GetObservationNumber(); int dts = aConfigurations.GetDenseTileSize(); int p_grid = aConfigurations.GetPGrid(); int q_grid = aConfigurations.GetQGrid(); @@ -315,11 +318,9 @@ void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfi template void LinearAlgebraMethods::GenerateSyntheticData(Configurations &aConfigurations, - const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, const kernels::Kernel &aKernel) { - this->mpContext = aHardware.GetChameleonContext(); this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(), aKernel.GetVariablesNumber()); auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); this->GenerateObservationsVector(aConfigurations, aData, aData->GetLocations(), aData->GetLocations(), @@ -334,7 +335,7 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig const kernels::Kernel &aKernel) { // Check for initialize the Chameleon context. - if (!this->mpContext) { + if (!ExaGeoStatHardware::GetChameleonContext()) { throw std::runtime_error( "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); } @@ -365,94 +366,94 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig theta[i] = aConfigurations.GetInitialTheta()[i]; } - VERBOSE("Initializing Covariance Matrix (Synthetic Dataset Generation Phase).....") + VERBOSE("\tInitializing Covariance Matrix (Synthetic Dataset Generation Phase).....") int upper_lower = EXAGEOSTAT_LOWER; START_TIMING(matrix_gen_time); - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_descC, upper_lower, apLocation1, apLocation2, - apLocation3, theta, aDistanceMetric, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_descC, upper_lower, apLocation1, + apLocation2, + apLocation3, theta, aDistanceMetric, &aKernel); ExaGeoStatSequenceWait(sequence); STOP_TIMING(matrix_gen_time); - VERBOSE("Done.") + VERBOSE("\tDone.") //Copy randomN to Z - VERBOSE("Generate Normal Random Distribution Vector Z (Synthetic Dataset Generation Phase) .....") + VERBOSE("\tGenerate Normal Random Distribution Vector Z (Synthetic Dataset Generation Phase) .....") auto *CHAM_descZ = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc; - CopyDescriptorZ(*aData->GetDescriptorData(), CHAM_descZ, randomN); + RuntimeFunctions::CopyDescriptorZ(*aData->GetDescriptorData(), CHAM_descZ, randomN); VERBOSE("Done.") //Cholesky factorization for the Co-variance matrix C - VERBOSE("Cholesky factorization of Sigma (Synthetic Dataset Generation Phase) .....") + VERBOSE("\tCholesky factorization of Sigma (Synthetic Dataset Generation Phase) .....") START_TIMING(time_facto); ExaGeoStatPotrfTile(EXAGEOSTAT_LOWER, CHAM_descC, 0, nullptr, nullptr, 0, 0); STOP_TIMING(time_facto); flops = flops + flops_dpotrf(full_problem_size); - VERBOSE("Done.") + VERBOSE("\tDone.") //Triangular matrix-matrix multiplication - VERBOSE("Triangular matrix-matrix multiplication Z=L.e (Synthetic Dataset Generation Phase) .....") + VERBOSE("\tTriangular matrix-matrix multiplication Z=L.e (Synthetic Dataset Generation Phase) .....") START_TIMING(time_trmm); ExaGeoStatTrmmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_NO_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_descC, CHAM_descZ); STOP_TIMING(time_trmm); flops = flops + flops_dtrmm(ChamLeft, full_problem_size, CHAM_descZ->n); - VERBOSE("Done.") + VERBOSE("\tDone.") if (aConfigurations.GetIsNonGaussian()) { //Gaussian to non-gaussian transformation VERBOSE("Convert Z Gaussian to non-Gaussian (Synthetic Dataset Generation Phase) .....") - ExaGeoStatGaussianToNonTileAsync(*aData->GetDescriptorData(), CHAM_descZ, theta); + RuntimeFunctions::ExaGeoStatGaussianToNonTileAsync(*aData->GetDescriptorData(), CHAM_descZ, theta); VERBOSE("Done.") } delete[] theta; if (aConfigurations.GetLogger()) { T *pMatrix; VERBOSE("Writing generated data to the disk (Synthetic Dataset Generation Phase) .....") -#ifdef CHAMELEON_USE_MPI +#ifdef USE_MPI pMatrix = new T[full_problem_size]; string path = aConfigurations.GetLoggerPath(); ExaGeoStatDesc2Lap(pMatrix, full_problem_size, CHAM_descZ, EXAGEOSTAT_UPPER_LOWER); if ( CHAMELEON_Comm_rank() == 0 ){ - helpers::DiskWriter::WriteVectorsToDisk(*pMatrix, full_problem_size, P, path, *apLocation1); + dataLoader::csv::CSVLoader::GetInstance()->WriteData(*pMatrix, full_problem_size, P, path, *apLocation1); } delete[] pMatrix; #else pMatrix = (T *) CHAM_descZ->mat; string path = aConfigurations.GetLoggerPath(); - helpers::DiskWriter::WriteVectorsToDisk(*pMatrix, full_problem_size, P, path, *apLocation1); + dataLoader::csv::CSVLoader::GetInstance()->WriteData(*pMatrix, full_problem_size, P, path, *apLocation1); #endif - VERBOSE("Done.") + VERBOSE("\tDone.") } ExaGeoStatLaSetTile(EXAGEOSTAT_UPPER_LOWER, 0, 0, CHAM_descC); delete[] randomN; - VERBOSE("Done Z Vector Generation Phase. (Chameleon Synchronous)") + VERBOSE("\tDone Z Vector Generation Phase. (Chameleon Synchronous)") int total_flops = flops / 1e9 / (time_facto + time_trmm); - LOGGER(" ---- Facto Time: " << time_facto) - LOGGER(" ---- dtrmm Time: " << time_trmm) - LOGGER(" ---- Matrix Generation Time: " << matrix_gen_time) - LOGGER(" ---- Total Time: " << time_facto + time_trmm) - LOGGER(" ---- Gflop/s: " << total_flops) - - results::Results::GetInstance()->SetTotalDataGenerationExecutionTime(time_facto + time_trmm); - results::Results::GetInstance()->SetTotalDataGenerationFlops(total_flops); - results::Results::GetInstance()->SetGeneratedLocationsNumber(full_problem_size / aConfigurations.GetTimeSlot()); - results::Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); - results::Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); + VERBOSE("\t---- Facto Time: " << time_facto) + VERBOSE("\t---- dtrmm Time: " << time_trmm) + VERBOSE("\t---- Matrix Generation Time: " << matrix_gen_time) + VERBOSE("\t---- Total Time: " << time_facto + time_trmm) + VERBOSE("\t---- Gflop/s: " << total_flops) + + Results::GetInstance()->SetTotalDataGenerationExecutionTime(time_facto + time_trmm); + Results::GetInstance()->SetTotalDataGenerationFlops(total_flops); + Results::GetInstance()->SetGeneratedLocationsNumber(full_problem_size / aConfigurations.GetTimeSlot()); + Results::GetInstance()->SetIsLogger(aConfigurations.GetLogger()); + Results::GetInstance()->SetLoggerPath(aConfigurations.GetLoggerPath()); } template T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, - T *apZActual, T *apZMiss, const ExaGeoStatHardware &aHardware, - Configurations &aConfiguration, Locations &aMissLocations, - Locations &aObsLocations, const kernels::Kernel &aKernel) { + T *apZActual, T *apZMiss, Configurations &aConfiguration, + Locations &aMissLocations, Locations &aObsLocations, + const kernels::Kernel &aKernel) { int i; - this->SetContext(aHardware.GetChameleonContext()); - this->InitiatePredictionDescriptors(aConfiguration, aData, aKernel.GetVariablesNumber()); + this->InitiatePredictionDescriptors(aConfiguration, aData); double time_solve, mat_gen_time, time_gemm, time_mspe = 0.0, flops = 0.0; int num_params; @@ -499,18 +500,18 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptrGetDescriptorData()->GetRequest(); //Copy data to vectors - VERBOSE("Copy measurements vector to descZobs descriptor...") + VERBOSE("\tCopy measurements vector to descZobs descriptor...") ExaGeoStatLap2Desc(apZObs, aZObsNumber, CHAM_desc_Zobs, UpperLower::EXAGEOSTAT_UPPER_LOWER); - VERBOSE("Done.") + VERBOSE("\tDone.") if (apZActual) { //Copy data to vectors - VERBOSE("Copy actual measurements vector to descZactual descriptor...") + VERBOSE("\tCopy actual measurements vector to descZactual descriptor...") ExaGeoStatLap2Desc(apZActual, aZMissNumber, CHAM_desc_Zactual, UpperLower::EXAGEOSTAT_UPPER_LOWER); - VERBOSE("Done.") + VERBOSE("\tDone.") } - LOGGER("- Estimated Parameters (", true) + LOGGER("\t\t- Estimated Theta (", true) for (i = 0; i < num_params; i++) { LOGGER_PRECISION(apTheta[i]) if (i != num_params - 1) { @@ -521,47 +522,51 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptrCovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_C22, upper_lower, &aObsLocations, - &aObsLocations, &median_locations, apTheta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_C22, upper_lower, &aObsLocations, + &aObsLocations, &median_locations, apTheta, 0, &aKernel); ExaGeoStatSequenceWait(sequence); VERBOSE("Done.") VERBOSE("Generate C12 Covariance Matrix... (Prediction Stage)") - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_C12, upper_lower, &aMissLocations, - &aObsLocations, &median_locations, apTheta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_C12, upper_lower, &aMissLocations, + &aObsLocations, &median_locations, apTheta, 0, &aKernel); ExaGeoStatSequenceWait(sequence); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(mat_gen_time); START_TIMING(time_solve); //Start prediction - VERBOSE("Calculate dposv C22 Covariance Matrix... (Prediction Stage)") + VERBOSE("\tCalculate dposv C22 Covariance Matrix... (Prediction Stage)") ExaGeoStatPosvTile(EXAGEOSTAT_LOWER, CHAM_desc_C22, CHAM_desc_Zobs); flops = flops + flops_dpotrf(aZObsNumber); flops = flops + flops_dtrsm(ChamLeft, aZObsNumber, aZObsNumber); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(time_solve); START_TIMING(time_gemm); - VERBOSE("Calculate dgemm Zmiss= C12 * Zobs Covariance Matrix... (Prediction Stage)") + VERBOSE("\tCalculate dgemm Zmiss= C12 * Zobs Covariance Matrix... (Prediction Stage)") CHAMELEON_dgemm_Tile(ChamNoTrans, ChamNoTrans, 1, CHAM_desc_C12, CHAM_desc_Zobs, 0, CHAM_desc_Zmiss); flops = flops + flops_dgemm(aZMissNumber, aZObsNumber, aZObsNumber); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(time_gemm); ExaGeoStatDesc2Lap(apZMiss, aZMissNumber, CHAM_desc_Zmiss, EXAGEOSTAT_UPPER_LOWER); if (apZActual) { START_TIMING(time_mspe); - VERBOSE("Calculate Mean Square Prediction Error (MSPE) ... (Prediction Stage)") + VERBOSE("\tCalculate Mean Square Prediction Error (MSPE) ... (Prediction Stage)") if (kernel_name == "BivariateMaternParsimonious") { - ExaGeoStatMLEMSPEBivariateTileAsync(CHAM_desc_Zactual, CHAM_desc_Zmiss, CHAM_desc_mspe1, CHAM_desc_mspe2, - CHAM_desc_mspe, sequence, &request_array[0]); + RuntimeFunctions::ExaGeoStatMLEMSPEBivariateTileAsync(CHAM_desc_Zactual, CHAM_desc_Zmiss, + CHAM_desc_mspe1, + CHAM_desc_mspe2, CHAM_desc_mspe, sequence, + &request_array[0]); } else { - this->ExaGeoStatMLEMSPETileAsync(CHAM_desc_Zactual, CHAM_desc_Zmiss, CHAM_desc_mspe, sequence, request); + RuntimeFunctions::ExaGeoStatMLEMSPETileAsync(CHAM_desc_Zactual, CHAM_desc_Zmiss, CHAM_desc_mspe, + sequence, + request); } ExaGeoStatSequenceWait(sequence); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(time_mspe); *mspe /= aZMissNumber; @@ -577,14 +582,16 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptrSetMSPEExecutionTime(time_solve + time_gemm); - results::Results::GetInstance()->SetMSPEFlops((flops / 1e9 / (time_solve + time_gemm))); - results::Results::GetInstance()->SetMSPEError(*mspe); + Results::GetInstance()->SetMSPEExecutionTime(time_solve + time_gemm); + Results::GetInstance()->SetMSPEFlops((flops / 1e9 / (time_solve + time_gemm))); + Results::GetInstance()->SetMSPEError(*mspe); T *all_mspe = new T[3]; all_mspe[0] = *mspe; @@ -599,17 +606,15 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< T *apTheta, const int &aZMissNumber, const int &aZObsNumber, T *apZObs, T *apZActual, T *apZMiss, - const ExaGeoStatHardware &aHardware, Configurations &aConfiguration, dataunits::Locations &aMissLocations, dataunits::Locations &aObsLocations, const kernels::Kernel &aKernel) { int i; - this->SetContext(aHardware.GetChameleonContext()); - this->InitiatePredictionDescriptors(aConfiguration, aData, aKernel.GetVariablesNumber()); + this->InitiatePredictionDescriptors(aConfiguration, aData); - double time_solve, mat_gen_time, time_mse, mat_gen_time_2, dposv_time, gemms_time, time_trsm, time_gemm, time_mspe = 0.0, flops = 0.0; + double time_solve, mat_gen_time, mat_gen_time_2, time_trsm, time_gemm, time_mspe = 0.0, flops = 0.0; int num_params; auto *CHAM_desc_Zmiss = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, @@ -632,8 +637,6 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< DescriptorName::DESCRIPTOR_R).chameleon_desc; auto *CHAM_desc_Rcopy = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_R_COPY).chameleon_desc; - auto *CHAM_desc_product = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_PRODUCT).chameleon_desc; T *mspe = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe); *mspe = 0; @@ -660,15 +663,15 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< void *request = aData->GetDescriptorData()->GetRequest(); //Copy data to vectors - VERBOSE("Copy measurements vector to descZobs descriptor...") + VERBOSE("\tCopy measurements vector to descZobs descriptor...") ExaGeoStatLap2Desc(apZObs, aZObsNumber, CHAM_desc_Zobs, UpperLower::EXAGEOSTAT_UPPER_LOWER); - VERBOSE("Done.") + VERBOSE("\tDone.") if (apZActual) { //Copy data to vectors - VERBOSE("Copy actual measurements vector to descZactual descriptor...") + VERBOSE("\tCopy actual measurements vector to descZactual descriptor...") ExaGeoStatLap2Desc(apZActual, aZMissNumber, CHAM_desc_Zactual, UpperLower::EXAGEOSTAT_UPPER_LOWER); - VERBOSE("Done.") + VERBOSE("\tDone.") } LOGGER("- Estimated Parameters (", true) @@ -682,54 +685,54 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< LOGGER("") //Convert the non-Gaussian observation to Gaussian - this->ExaGeoStatNonGaussianTransformTileAsync(CHAM_desc_Zobs, CHAM_desc_product, apTheta, sequence, - &request_array[0]); + RuntimeFunctions::ExaGeoStatNonGaussianTransformTileAsync(aConfiguration.GetComputation(), CHAM_desc_Zobs, + apTheta, sequence, &request_array[0]); START_TIMING(mat_gen_time); - VERBOSE("Generate R_theta Covariance Matrix... (Prediction Stage)") + VERBOSE("\tGenerate R_theta Covariance Matrix... (Prediction Stage)") int upper_lower = EXAGEOSTAT_LOWER; - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_C22, upper_lower, &aObsLocations, - &aObsLocations, &median_locations, apTheta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_C22, upper_lower, &aObsLocations, + &aObsLocations, &median_locations, apTheta, 0, &aKernel); ExaGeoStatSequenceWait(sequence); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(mat_gen_time); START_TIMING(time_solve); - VERBOSE("Calculate dposv R_theta Covariance Matrix... (Prediction Stage)"); + VERBOSE("Calculate dposv R_theta Covariance Matrix... (Prediction Stage)") ExaGeoStatPosvTile(EXAGEOSTAT_LOWER, CHAM_desc_C22, CHAM_desc_Zobs); flops = flops + flops_dpotrf(aZObsNumber); flops = flops + 2 * flops_dtrsm(ChamLeft, CHAM_desc_C22->m, CHAM_desc_Zobs->n); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(time_solve); START_TIMING(mat_gen_time_2); VERBOSE("Generate R_theta Covariance Matrix... (Prediction Stage)") - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_R, upper_lower, &aObsLocations, - &aMissLocations, &median_locations, apTheta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_R, upper_lower, &aObsLocations, + &aMissLocations, &median_locations, apTheta, 0, &aKernel); ExaGeoStatSequenceWait(sequence); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(mat_gen_time_2); ExaGeoStatLapackCopyTile(EXAGEOSTAT_LOWER, CHAM_desc_R, CHAM_desc_Rcopy); START_TIMING(time_trsm); - VERBOSE("Calculate dposv r_theta Covariance Matrix... (Prediction Stage)"); + VERBOSE("Calculate dposv r_theta Covariance Matrix... (Prediction Stage)") ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_NO_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_C22, nullptr, nullptr, CHAM_desc_R, 0); ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_C22, nullptr, nullptr, CHAM_desc_R, 0); flops = flops + 2 * flops_dtrsm(ChamLeft, CHAM_desc_C22->m, CHAM_desc_Zobs->n); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(time_trsm); START_TIMING(time_gemm); - VERBOSE("For each missing location, Generate correlation vector CHAMELEON_descr (Prediction Stage) ....."); + VERBOSE("For each missing location, Generate correlation vector CHAMELEON_descr (Prediction Stage) .....") CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Rcopy, CHAM_desc_Zobs, 0, CHAM_desc_Zmiss); CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Rcopy, CHAM_desc_R, 0, CHAM_desc_C12); STOP_TIMING(time_gemm); auto r = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_C12); - auto Zmiss2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_Zmiss);; + auto Zmiss2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_Zmiss); auto ng_nu = new T[aZMissNumber]; auto ng_sigma_sq = new T[aZMissNumber]; @@ -748,23 +751,24 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< (exp((pow(apTheta[4], 2) * sigma_sq + 2 * apTheta[4] * mu) / (2 * (1 - apTheta[5] * sigma_sq))) - 1); } - VERBOSE(" Done.") + VERBOSE("\t Done.") ExaGeoStatLap2Desc(apZMiss, aZMissNumber, CHAM_desc_Zmiss, EXAGEOSTAT_UPPER_LOWER); if (apZActual != nullptr) { START_TIMING(time_mspe); - VERBOSE("Calculate Mean Square Error (MSE) ... (Prediction Stage) \n"); - this->ExaGeoStatMLEMSPETileAsync(CHAM_desc_Zactual, CHAM_desc_Zmiss, CHAM_desc_mspe, sequence, request); + VERBOSE("Calculate Mean Square Error (MSE) ... (Prediction Stage) \n") + RuntimeFunctions::ExaGeoStatMLEMSPETileAsync(CHAM_desc_Zactual, CHAM_desc_Zmiss, CHAM_desc_mspe, sequence, + request); ExaGeoStatSequenceWait(sequence); - VERBOSE(" Done.") + VERBOSE("\t Done.") STOP_TIMING(time_mspe); *mspe /= aZMissNumber; } else { *mspe = -1; } - if(helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) { + if (helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) { if (aConfiguration.GetLogger()) { fprintf(aConfiguration.GetFileLogPath(), "\n\n# of missing observations :%d\n\nPrediction Execution Time: %.8f, ""Flops: %.8f, Mean Square Prediction Error (MSPE): %.8f\n\n", @@ -772,14 +776,14 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< (flops / 1e9 / (time_solve)), *mspe); } - VERBOSE("- Z Actual .. Z Miss") + VERBOSE("\t- Z Actual .. Z Miss") for (i = 0; i < aZMissNumber; i++) { - VERBOSE(" (" << apZActual[i] << ", " << apZMiss[i] << ")") + VERBOSE("\t (" << apZActual[i] << ", " << apZMiss[i] << ")") } - results::Results::GetInstance()->SetMSPEExecutionTime(time_solve + time_gemm + time_trsm); - results::Results::GetInstance()->SetMSPEFlops((flops / 1e9 / (time_solve + time_gemm))); - results::Results::GetInstance()->SetMSPEError(*mspe); + Results::GetInstance()->SetMSPEExecutionTime(time_solve + time_gemm + time_trsm); + Results::GetInstance()->SetMSPEFlops((flops / 1e9 / (time_solve + time_gemm))); + Results::GetInstance()->SetMSPEError(*mspe); } T *all_mspe = new T[3]; @@ -794,12 +798,10 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< template void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigurations, std::unique_ptr> &aData, - const ExaGeoStatHardware &aHardware, T *apTruthTheta, - T *apEstimatedTheta, Locations &aMissLocations, - Locations &aObsLocations, + T *apTruthTheta, T *apEstimatedTheta, + Locations &aMissLocations, Locations &aObsLocations, const kernels::Kernel &aKernel) { - this->SetContext(aHardware.GetChameleonContext()); this->InitiateMLOEMMOMDescriptors(aConfigurations, aData, aKernel.GetVariablesNumber()); auto kernel_name = aConfigurations.GetKernelName(); auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); @@ -807,12 +809,12 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu int n_z_miss = aConfigurations.GetUnknownObservationsNb(); int num_par = aKernel.GetParametersNumbers(); - LOGGER("- Truth Theta: ", true) + LOGGER("\t\t- Truth Theta: ", true) for (int num = 0; num < num_par; num++) { LOGGER_PRECISION(apTruthTheta[num] << " ") } LOGGER("") - LOGGER("- Estimated Theta: ", true) + LOGGER("\t\t- Estimated Theta: ", true) for (int num = 0; num < num_par; num++) { LOGGER_PRECISION(apEstimatedTheta[num] << " ") } @@ -913,76 +915,76 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu this->ExaGeoStatLap2Desc(estimated_alpha, m, CHAM_desc_estimated_alpha, EXAGEOSTAT_UPPER_LOWER); START_TIMING(matrix_gen); - VERBOSE("Create K_a and K_t Covariance Matrices (MLOE-MMOM).....") + VERBOSE("\tCreate K_a and K_t Covariance Matrices (MLOE-MMOM).....") int upper_lower = EXAGEOSTAT_LOWER; - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_K_a, upper_lower, &aObsLocations, - &aObsLocations, &median_locations, apEstimatedTheta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_K_a, upper_lower, &aObsLocations, + &aObsLocations, &median_locations, apEstimatedTheta, 0, &aKernel); this->ExaGeoStatSequenceWait(sequence); - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_K_t, upper_lower, &aObsLocations, - &aObsLocations, &median_locations, apTruthTheta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_K_t, upper_lower, &aObsLocations, + &aObsLocations, &median_locations, apTruthTheta, 0, &aKernel); this->ExaGeoStatSequenceWait(sequence); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(matrix_gen); //Cholesky factorization for the Co-variance matrix CHAM_desc_K_a START_TIMING(cholesky1); - VERBOSE("Cholesky factorization of CHAM_desc_K_a (MLOE-MMOM) .....") + VERBOSE("\tCholesky factorization of CHAM_desc_K_a (MLOE-MMOM) .....") ExaGeoStatPotrfTile(EXAGEOSTAT_LOWER, CHAM_desc_K_a, aConfigurations.GetBand(), nullptr, nullptr, 0, 0); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(cholesky1); flops = flops + flops_dpotrf(CHAM_desc_K_a->m); START_TIMING(cholesky2); //Cholesky factorization for the Co-variance matrix CHAM_desc_K_t - VERBOSE("Cholesky factorization of CHAM_desc_K_t (MLOE-MMOM) .....") + VERBOSE("\tCholesky factorization of CHAM_desc_K_t (MLOE-MMOM) .....") ExaGeoStatPotrfTile(EXAGEOSTAT_LOWER, CHAM_desc_K_t, aConfigurations.GetBand(), nullptr, nullptr, 0, 0); - VERBOSE("Done.") + VERBOSE("\tDone.") STOP_TIMING(cholesky2); flops = flops + flops_dpotrf(CHAM_desc_K_t->m); T total_loop_time = 0.0; T loop_time; bool verbose; - VERBOSE("* Verbose messages are printed every 10 iteration *") + VERBOSE("\t* Verbose messages are printed every 10 iteration *") for (p = 0; p < n_z_miss; p++) { verbose = p % 10 == 0; lmiss->GetLocationX()[0] = aMissLocations.GetLocationX()[p]; lmiss->GetLocationY()[0] = aMissLocations.GetLocationY()[p]; if (verbose) { - VERBOSE("Generate two vectors k_a and k_t (MLOE-MMOM).....") + VERBOSE("\tGenerate two vectors k_a and k_t (MLOE-MMOM).....") } START_TIMING(vecs_gen); upper_lower = EXAGEOSTAT_UPPER_LOWER; - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_k_t, upper_lower, &aObsLocations, lmiss, - &median_locations, apTruthTheta, 0, &aKernel); - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_k_a, upper_lower, &aObsLocations, lmiss, - &median_locations, apEstimatedTheta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_k_t, upper_lower, &aObsLocations, + lmiss, &median_locations, apTruthTheta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_k_a, upper_lower, &aObsLocations, + lmiss, &median_locations, apEstimatedTheta, 0, &aKernel); this->ExaGeoStatSequenceWait(sequence); STOP_TIMING(vecs_gen); //Copy CHAM_desc_k_a to CHAM_descK_atmp (MLOE-MMOM) if (verbose) { - VERBOSE("Copy CHAM_desc_k_a to CHAM_descK_atmp (MLOE-MMOM).....") + VERBOSE("\tCopy CHAM_desc_k_a to CHAM_descK_atmp (MLOE-MMOM).....") } START_TIMING(copy_vecs); ExaGeoStatLapackCopyTile(EXAGEOSTAT_UPPER_LOWER, CHAM_desc_k_t, CHAM_desc_k_t_tmp); ExaGeoStatLapackCopyTile(EXAGEOSTAT_UPPER_LOWER, CHAM_desc_k_a, CHAM_desc_k_a_tmp); STOP_TIMING(copy_vecs); if (verbose) { - VERBOSE("Done.") + VERBOSE("\tDone.") } START_TIMING(loop_time); START_TIMING(trsm1); // Triangular Solve (TRSM) k_a = TRSM(L_a^-1, k_a) if (verbose) { - VERBOSE("Solving the linear system k_a = TRSM(l_a^-1, k_a) ...(MLOE-MMOM)") + VERBOSE("\tSolving the linear system k_a = TRSM(l_a^-1, k_a) ...(MLOE-MMOM)") } ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_NO_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_K_a, nullptr, nullptr, CHAM_desc_k_a, 0); if (verbose) { - VERBOSE("Done.") + VERBOSE("\tDone.") } flops = flops + flops_dtrsm(ChamLeft, CHAM_desc_K_a->m, CHAM_desc_k_a->n); STOP_TIMING(trsm1); @@ -990,74 +992,74 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu START_TIMING(trsm2); // Triangular Solve (TRSM) k_t = TRSM(L_t^-1, k_t) if (verbose) { - VERBOSE("Solving the linear system k_t = TRSM(L_t^-1, k_t) ...(MLOE-MMOM)") + VERBOSE("\tSolving the linear system k_t = TRSM(L_t^-1, k_t) ...(MLOE-MMOM)") } ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_NO_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_K_t, nullptr, nullptr, CHAM_desc_k_t, 0); flops = flops + flops_dtrsm(ChamLeft, CHAM_desc_K_t->m, CHAM_desc_k_t->n); if (verbose) { - VERBOSE("Done.") + VERBOSE("\tDone.") } STOP_TIMING(trsm2); START_TIMING(trsm3); // Triangular Solve (TRSM) k_a = TRSM(L_a^-T, k_a) if (verbose) { - VERBOSE("Solving the linear system k_a = TRSM(L_a^-T, k_a) ...(MLOE-MMOM)") + VERBOSE("\tSolving the linear system k_a = TRSM(L_a^-T, k_a) ...(MLOE-MMOM)") } ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_K_a, nullptr, nullptr, CHAM_desc_k_a, 0); flops = flops + flops_dtrsm(ChamLeft, CHAM_desc_K_a->m, CHAM_desc_k_a->n); if (verbose) { - VERBOSE("Done.") + VERBOSE("\tDone.") } STOP_TIMING(trsm3); START_TIMING(trsm4); // Triangular Solve (TRSM) k_t = TRSM(L_t^-T, k_t) if (verbose) { - VERBOSE("Solving the linear system k_t = TRSM(L_a^-T, k_t) ...(MLOE-MMOM)") + VERBOSE("\tSolving the linear system k_t = TRSM(L_a^-T, k_t) ...(MLOE-MMOM)") } ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_K_t, nullptr, nullptr, CHAM_desc_k_t, 0); flops = flops + flops_dtrsm(ChamLeft, CHAM_desc_K_t->m, CHAM_desc_k_t->n); if (verbose) { - VERBOSE("Done.") + VERBOSE("\tDone.") } STOP_TIMING(trsm4); START_TIMING(gevv2); // Calculate dgemm value= CHAM_desc_k_t^T * CHAM_desc_k_a if (verbose) { - VERBOSE("Calculate dgemm CHAM_desc_expr1 = CHAM_desc_k_t^T * CHAM_desc_k_a... (MLOE-MMOM)") + VERBOSE("\tCalculate dgemm CHAM_desc_expr1 = CHAM_desc_k_t^T * CHAM_desc_k_a... (MLOE-MMOM)") } CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_k_t_tmp, CHAM_desc_k_a, 0, CHAM_desc_expr1); flops = flops + flops_dgemm(CHAM_desc_k_t_tmp->m, CHAM_desc_k_a->n, CHAM_desc_expr1->n); if (verbose) { - VERBOSE("Done.") + VERBOSE("\tDone.") } STOP_TIMING(gevv2); START_TIMING(gevv3); // Calculate dgemm value= CHAM_desc_k_a^T * CHAM_desc_k_a_tmp if (verbose) { - VERBOSE("Calculate dgemm CHAM_desc_expr1 = CHAM_desc_k_a^T * CHAM_desc_k_a... (MLOE-MMOM)") + VERBOSE("\tCalculate dgemm CHAM_desc_expr1 = CHAM_desc_k_a^T * CHAM_desc_k_a... (MLOE-MMOM)") } CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_k_a_tmp, CHAM_desc_k_a, 0, CHAM_desc_expr4); flops = flops + flops_dgemm(CHAM_desc_k_a_tmp->m, CHAM_desc_k_a->n, CHAM_desc_expr4->n); if (verbose) { - VERBOSE("Done.") + VERBOSE("\tDone.") } STOP_TIMING(gevv3); START_TIMING(gevv1); // Calculate dgemm value= CHAM_desc_k_a^T * CHAM_desc_k_t if (verbose) { - VERBOSE("Calculate dgemm CHAM_desc_expr4 = CHAM_desc_k_a^T * CHAM_desc_k_t... (Prediction Stage)") + VERBOSE("\tCalculate dgemm CHAM_desc_expr4 = CHAM_desc_k_a^T * CHAM_desc_k_t... (Prediction Stage)") } CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_k_t_tmp, CHAM_desc_k_t, 0, CHAM_desc_expr3); flops = flops + flops_dgemm(CHAM_desc_k_t_tmp->m, CHAM_desc_k_t->n, CHAM_desc_expr3->n); if (verbose) { - VERBOSE("Done.") + VERBOSE("\tDone.") } STOP_TIMING(gevv1); @@ -1069,12 +1071,12 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu // Calculate dgemm value= CHAM_desc_k_a^T * CHAM_desc_k_t if (verbose) { - VERBOSE("Calculate dgemm CHAM_desc_expr1 = CHAM_desc_k_a^T * CHAM_desc_k_a... (Prediction Stage)") + VERBOSE("\tCalculate dgemm CHAM_desc_expr1 = CHAM_desc_k_a^T * CHAM_desc_k_a... (Prediction Stage)") } CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_k_a, CHAM_desc_k_a, 0, CHAM_desc_expr2); flops = flops + flops_dgemm(CHAM_desc_k_a_tmp->m, CHAM_desc_k_t->n, CHAM_desc_expr2->n); if (verbose) { - VERBOSE("Done.") + VERBOSE("\tDone.") } START_TIMING(gevv5); STOP_TIMING(gevv5); @@ -1087,33 +1089,32 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu ExaGeoStatGeaddTile(EXAGEOSTAT_NO_TRANS, 1, CHAM_desc_truth_alpha, -1, CHAM_desc_expr3); ExaGeoStatGeaddTile(EXAGEOSTAT_NO_TRANS, 1, CHAM_desc_estimated_alpha, -1, CHAM_desc_expr4); - VERBOSE("- Matrix Generation Time: " << matrix_gen << " Vectors Generation Time: " << vecs_gen + VERBOSE("\t- Matrix Generation Time: " << matrix_gen << " Vectors Generation Time: " << vecs_gen << " First Cholesky factorization Time: " << cholesky1 << " First Cholesky factorization Time: " << cholesky2) - VERBOSE("- First Trsm time: " << trsm1 << " Second Trsm time: " << trsm2 << " Third Trsm time: " << trsm3 + VERBOSE("\t- First Trsm time: " << trsm1 << " Second Trsm time: " << trsm2 << " Third Trsm time: " << trsm3 << " Fourth Trsm time: " << trsm4) - VERBOSE("- First gemm time: " << gevv1 << " Second gemm time: " << gevv2 << " Third gemm time: " << gevv3 + VERBOSE("\t- First gemm time: " << gevv1 << " Second gemm time: " << gevv2 << " Third gemm time: " << gevv3 << " Fourth gemm time: " << gevv4 << " Fifth gemm time: " << gevv5) - ExaGeoStatMLETileAsyncMLOEMMOM(CHAM_desc_expr2, CHAM_desc_expr3, CHAM_desc_expr4, CHAM_desc_mloe, - CHAM_desc_mmom, sequence, request); + RuntimeFunctions::ExaGeoStatMLETileAsyncMLOEMMOM(CHAM_desc_expr2, CHAM_desc_expr3, CHAM_desc_expr4, + CHAM_desc_mloe, CHAM_desc_mmom, sequence, request); this->ExaGeoStatSequenceWait(sequence); } - LOGGER(" ---- MLOE-MMOM Gflop/s: " << flops / 1e9 / (total_loop_time + cholesky1 + cholesky2)) + VERBOSE("\t---- MLOE-MMOM Gflop/s: " << flops / 1e9 / (total_loop_time + cholesky1 + cholesky2)) *mloe /= n_z_miss; *mmom /= n_z_miss; STOP_TIMING(all_time); - LOGGER(" ---- MLOE = " << *mloe) - LOGGER(" ---- MMOM = " << *mmom) - LOGGER(" ---- MLOE MMOM Time: " << all_time << " seconds.") - - results::Results::GetInstance()->SetMLOE(*mloe); - results::Results::GetInstance()->SetMMOM(*mmom); - results::Results::GetInstance()->SetExecutionTimeMLOEMMOM(all_time); - results::Results::GetInstance()->SetMatrixGenerationTimeMLOEMMOM(matrix_gen); - results::Results::GetInstance()->SetFactoTimeMLOEMMOM(cholesky1 + cholesky2); - results::Results::GetInstance()->SetLoopTimeMLOEMMOM(total_loop_time); - results::Results::GetInstance()->SetFlopsMLOEMMOM((flops / 1e9 / (total_loop_time + cholesky1 + cholesky2))); + LOGGER("\t\t- MLOE = " << *mloe << "\t\t- MMOM = " << *mmom) + VERBOSE("\t---- MLOE MMOM Time: " << all_time << " seconds.") + + Results::GetInstance()->SetMLOE(*mloe); + Results::GetInstance()->SetMMOM(*mmom); + Results::GetInstance()->SetExecutionTimeMLOEMMOM(all_time); + Results::GetInstance()->SetMatrixGenerationTimeMLOEMMOM(matrix_gen); + Results::GetInstance()->SetFactoTimeMLOEMMOM(cholesky1 + cholesky2); + Results::GetInstance()->SetLoopTimeMLOEMMOM(total_loop_time); + Results::GetInstance()->SetFlopsMLOEMMOM((flops / 1e9 / (total_loop_time + cholesky1 + cholesky2))); delete[] loe; delete[] mom; @@ -1127,11 +1128,9 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu template T *LinearAlgebraMethods::ExaGeoStatFisherTile(Configurations &aConfigurations, - std::unique_ptr> &aData, - const ExaGeoStatHardware &aHardware, T *apTheta, + std::unique_ptr> &aData, T *apTheta, const kernels::Kernel &aKernel) { - this->SetContext(aHardware.GetChameleonContext()); this->InitiateFisherDescriptors(aConfigurations, *aData->GetDescriptorData()); auto *CHAM_desc_A = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, @@ -1162,26 +1161,23 @@ T *LinearAlgebraMethods::ExaGeoStatFisherTile(Configurations &aConfigurations sequence = (RUNTIME_sequence_t *) aData->GetDescriptorData()->GetSequence(); } - void *request = aData->GetDescriptorData()->GetRequest(); - auto kernel_name = aConfigurations.GetKernelName(); int num_params = aKernel.GetParametersNumbers(); auto median_locations = Locations(1, aData->GetLocations()->GetDimension()); aData->CalculateMedianLocations(kernel_name, median_locations); - double time = 0.0; + double time; START_TIMING(time); VERBOSE("Generate covariance matrix CHAM_desc_C (Fisher Matrix Generation).....") - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_C, EXAGEOSTAT_LOWER, aData->GetLocations(), - aData->GetLocations(), &median_locations, apTheta, - aConfigurations.GetDistanceMetric(), - &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_C, EXAGEOSTAT_LOWER, + aData->GetLocations(), aData->GetLocations(), &median_locations, apTheta, + aConfigurations.GetDistanceMetric(), &aKernel); ExaGeoStatSequenceWait(sequence); - VERBOSE("Done.") + VERBOSE("\tDone.") - VERBOSE("Calculate Cholesky decomposition (Fisher Matrix Generation).....") + VERBOSE("\tCalculate Cholesky decomposition (Fisher Matrix Generation).....") ExaGeoStatPotrfTile(EXAGEOSTAT_LOWER, CHAM_desc_C, 0, nullptr, nullptr, 0, 0); - VERBOSE("Done.") + VERBOSE("\tDone.") //Allocate memory for A, and initialize it with 0s. auto A = new T[num_params * num_params](); @@ -1202,20 +1198,20 @@ T *LinearAlgebraMethods::ExaGeoStatFisherTile(Configurations &aConfigurations aConfigurations.GetTimeSlot()); VERBOSE("Generate covariance matrix CHAM_desc_CJ (Fisher Matrix Generation).....") - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_CJ, EXAGEOSTAT_UPPER_LOWER, - aData->GetLocations(), aData->GetLocations(), &median_locations, apTheta, - aConfigurations.GetDistanceMetric(), pKernel_cj); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_CJ, EXAGEOSTAT_UPPER_LOWER, + aData->GetLocations(), aData->GetLocations(), &median_locations, apTheta, + aConfigurations.GetDistanceMetric(), pKernel_cj); ExaGeoStatSequenceWait(sequence); - VERBOSE("Done.") + VERBOSE("\tDone.") - VERBOSE("Compute triangular solve CHAM_desc_CJ (Fisher Matrix Generation).....") + VERBOSE("\tCompute triangular solve CHAM_desc_CJ (Fisher Matrix Generation).....") ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_NO_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_C, nullptr, nullptr, CHAM_desc_CJ, 0); ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_C, nullptr, nullptr, CHAM_desc_CJ, 0); - VERBOSE("Done.") + VERBOSE("\tDone.") delete pKernel_cj; for (int k = j; k < num_params; k++) { @@ -1234,30 +1230,31 @@ T *LinearAlgebraMethods::ExaGeoStatFisherTile(Configurations &aConfigurations aConfigurations.GetTimeSlot()); VERBOSE("Generate covariance matrix CHAM_desc_CK (Fisher Matrix Generation).....") - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_CK, EXAGEOSTAT_UPPER_LOWER, - aData->GetLocations(), aData->GetLocations(), &median_locations, apTheta, - aConfigurations.GetDistanceMetric(), pKernel_ck); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_CK, EXAGEOSTAT_UPPER_LOWER, + aData->GetLocations(), aData->GetLocations(), &median_locations, + apTheta, aConfigurations.GetDistanceMetric(), pKernel_ck); ExaGeoStatSequenceWait(sequence); - VERBOSE("Done.") + VERBOSE("\tDone.") - VERBOSE("Compute tringular solve CHAM_desc_CK (Fisher Matrix Generation).....") + VERBOSE("\tCompute tringular solve CHAM_desc_CK (Fisher Matrix Generation).....") ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_NO_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_C, nullptr, nullptr, CHAM_desc_CK, 0); ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_C, nullptr, nullptr, CHAM_desc_CK, 0); - VERBOSE("Done.") + VERBOSE("\tDone.") - VERBOSE("Compute matrix-matrix multiplication CHAM_desc_CK (Fisher Matrix Generation).....") + VERBOSE("\tCompute matrix-matrix multiplication CHAM_desc_CK (Fisher Matrix Generation).....") CHAMELEON_dgemm_Tile(ChamNoTrans, ChamNoTrans, 1, CHAM_desc_CJ, CHAM_desc_CK, 0, CHAM_desc_results); - VERBOSE("Done.") + VERBOSE("\tDone.") VERBOSE("Compute the trace/diagonal of CHAM_desc_CK (Fisher Matrix Generation).....") - ExaGeoStatMLETraceTileAsync(CHAM_desc_results, sequence, &request_array[0], CHAM_desc_C_trace, - CHAM_desc_C_diag); + RuntimeFunctions::ExaGeoStatMLETraceTileAsync(CHAM_desc_results, sequence, &request_array[0], + CHAM_desc_C_trace, + CHAM_desc_C_diag); ExaGeoStatSequenceWait(sequence); - VERBOSE("Done.") + VERBOSE("\tDone.") A[k + num_params * j] = 0.5 * *trace; *trace = 0; @@ -1267,19 +1264,19 @@ T *LinearAlgebraMethods::ExaGeoStatFisherTile(Configurations &aConfigurations STOP_TIMING(time); - VERBOSE("Copy A array to descriptor CHAM_desc_A (Fisher Matrix Generation).....") + VERBOSE("\tCopy A array to descriptor CHAM_desc_A (Fisher Matrix Generation).....") ExaGeoStatLap2Desc(A, num_params, CHAM_desc_A, EXAGEOSTAT_UPPER_LOWER); - VERBOSE("Done.") + VERBOSE("\tDone.") - VERBOSE("Calculate Cholesky decomposition (Fisher Matrix Generation).....") + VERBOSE("\tCalculate Cholesky decomposition (Fisher Matrix Generation).....") LAPACKE_dpotrf(LAPACK_COL_MAJOR, 'L', num_params, (double *) A, num_params); - VERBOSE("Done.") - VERBOSE("Generate Identity Matrix (I) (Fisher Matrix Generation).....") + VERBOSE("\tDone.") + VERBOSE("\tGenerate Identity Matrix (I) (Fisher Matrix Generation).....") //Allocate memory for A, and initialize it with 0s. auto I_matrix = new T[num_params * num_params + 1](); LAPACKE_dlaset(LAPACK_COL_MAJOR, 'L', num_params, num_params, 0, 1, (double *) I_matrix, num_params); - VERBOSE("Done.") + VERBOSE("\tDone.") cblas_dtrsm( CblasColMajor, @@ -1299,11 +1296,7 @@ T *LinearAlgebraMethods::ExaGeoStatFisherTile(Configurations &aConfigurations I_matrix[num_params * num_params] = time; - results::Results::GetInstance()->SetTotalFisherTime(time); - results::Results::GetInstance()->SetFisher00((T) I_matrix[0]); - results::Results::GetInstance()->SetFisher11((T) I_matrix[4]); - results::Results::GetInstance()->SetFisher22((T) I_matrix[8]); - + Results::GetInstance()->SetTotalFisherTime(time); delete[] A; return I_matrix; } @@ -1342,305 +1335,6 @@ LinearAlgebraMethods::ExaGeoStatGetZObs(Configurations &aConfigurations, T *a this->ExaGeoStatDesc2Lap(apZ, aSize, z_desc, UpperLower::EXAGEOSTAT_UPPER_LOWER); } -template -void LinearAlgebraMethods::CovarianceMatrixCodelet(DescriptorData &aDescriptorData, void *apDescriptor, - const int &aTriangularPart, Locations *apLocation1, - Locations *apLocation2, Locations *apLocation3, - T *apLocalTheta, const int &aDistanceMetric, - const kernels::Kernel *apKernel) { - - // Check for initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - RUNTIME_option_t options; - RUNTIME_options_init((RUNTIME_option_t *) &options, (CHAM_context_t *) this->mpContext, - (RUNTIME_sequence_t *) aDescriptorData.GetSequence(), - (RUNTIME_request_t *) aDescriptorData.GetRequest()); - - int temp, tempnn; - auto *CHAM_apDescriptor = (CHAM_desc_t *) apDescriptor; - struct starpu_codelet *cl = &this->cl_dcmg; - int m, n, m0 = 0, n0 = 0; - - for (n = 0; n < CHAM_apDescriptor->nt; n++) { - tempnn = n == CHAM_apDescriptor->nt - 1 ? CHAM_apDescriptor->n - n * CHAM_apDescriptor->nb - : CHAM_apDescriptor->nb; - if (aTriangularPart == ChamUpperLower) { - m = 0; - } else { - m = CHAM_apDescriptor->m == CHAM_apDescriptor->n ? n : 0; - } - for (; m < CHAM_apDescriptor->mt; m++) { - temp = m == CHAM_apDescriptor->mt - 1 ? CHAM_apDescriptor->m - m * CHAM_apDescriptor->mb - : CHAM_apDescriptor->mb; - m0 = m * CHAM_apDescriptor->mb; - n0 = n * CHAM_apDescriptor->nb; - // Register the data with StarPU - starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), - STARPU_VALUE, &tempnn, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_VALUE, &n0, sizeof(int), - STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr(CHAM_apDescriptor, m, n), - STARPU_VALUE, &apLocation1, sizeof(Locations *), - STARPU_VALUE, &apLocation2, sizeof(Locations *), - STARPU_VALUE, &apLocation3, sizeof(Locations *), - STARPU_VALUE, &apLocalTheta, sizeof(double *), - STARPU_VALUE, &aDistanceMetric, sizeof(int), - STARPU_VALUE, &apKernel, sizeof(kernels::Kernel *), - 0); - } - } - - RUNTIME_options_ws_free(&options); - RUNTIME_options_finalize(&options, (CHAM_context_t *) this->mpContext); -} - -template -void -LinearAlgebraMethods::ExaGeoStatMLETraceTileAsync(void *apDescA, void *apSequence, void *apRequest, void *apDescNum, - void *apDescTrace) { - // Check for initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - - RUNTIME_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - int m, m0, n0; - int tempmm; - auto A = *((CHAM_desc_t *) apDescA); - struct starpu_codelet *cl = &this->cl_dtrace; - - for (m = 0; m < A.mt; m++) { - tempmm = m == A.mt - 1 ? A.m - m * A.mb : A.mb; - starpu_insert_task(cl, - STARPU_VALUE, &tempmm, sizeof(int), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescA, m, m), - STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescNum, 0, 0), - STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescTrace, m, 0), - 0); - } - - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, this->mpContext); -} - -template -void -LinearAlgebraMethods::ExaGeoStatGaussianToNonTileAsync(DescriptorData &aDescriptorData, void *apDesc, - T *apTheta) { - - // Check for initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - - RUNTIME_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, aDescriptorData.GetSequence(), aDescriptorData.GetRequest()); - - int m, m0; - int temp; - auto desc_z = (CHAM_desc_t *) apDesc; - struct starpu_codelet *cl = &this->cl_gaussian_to_non; - - - for (m = 0; m < desc_z->mt; m++) { - temp = m == desc_z->mt - 1 ? desc_z->m - m * desc_z->mb : desc_z->mb; - m0 = m * desc_z->mb; - - starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr(desc_z, m, 0), - STARPU_VALUE, &apTheta[0], sizeof(T), - STARPU_VALUE, &apTheta[1], sizeof(T), - STARPU_VALUE, &apTheta[2], sizeof(T), - STARPU_VALUE, &apTheta[3], sizeof(T), - STARPU_VALUE, &apTheta[4], sizeof(T), - STARPU_VALUE, &apTheta[5], sizeof(T), -#if defined(CHAMELEON_CODELETS_HAVE_NAME) - STARPU_NAME, "gaussian_to_non", -#endif - 0); - } - - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, this->mpContext); -} - -template -int LinearAlgebraMethods::ExaGeoStatMLEMSPETileAsync(void *apDescZPredict, void *apDescZMiss, - void *apDescError, void *apSequence, - void *apRequest) { - // Check for Initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - - RUNTIME_option_t options; - ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - int m, m0; - int temp; - auto Zpre = (CHAM_desc_t *) apDescZPredict; - struct starpu_codelet *cl = &this->cl_dmse; - - for (m = 0; m < Zpre->mt; m++) { - temp = m == Zpre->mt - 1 ? Zpre->m - m * Zpre->mb : Zpre->mb; - - m0 = m * Zpre->mb; - starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescError, 0, 0), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZPredict, m, 0), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZMiss, m, 0), -#if defined(CHAMELEON_CODELETS_HAVE_NAME) - STARPU_NAME, "dmse", -#endif - 0); - } - ExaGeoStatOptionsFree(&options); - ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); - return CHAMELEON_SUCCESS; -} - -template -int LinearAlgebraMethods::ExaGeoStatMLETileAsyncMLOEMMOM(void *apDescExpr2, void *apDescExpr3, void *apDescExpr4, - void *apDescMLOE, void *apDescMMOM, void *apSequence, - void *apRequest) { - - // Check for Initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - - RUNTIME_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - int m, n, m0, n0; - struct starpu_codelet *cl = &this->cl_dmloe_mmom; - int temp, temp2; - - for (n = 0; n < ((CHAM_desc_t *) apDescExpr2)->nt; n++) { - temp2 = n == ((CHAM_desc_t *) apDescExpr2)->nt - 1 ? ((CHAM_desc_t *) apDescExpr2)->n - - n * ((CHAM_desc_t *) apDescExpr2)->nb - : ((CHAM_desc_t *) apDescExpr2)->nb; - for (m = 0; m < ((CHAM_desc_t *) apDescExpr2)->mt; m++) { - - temp = m == ((CHAM_desc_t *) apDescExpr2)->mt - 1 ? ((CHAM_desc_t *) apDescExpr2)->m - - m * ((CHAM_desc_t *) apDescExpr2)->mb - : ((CHAM_desc_t *) apDescExpr2)->mb; - m0 = m * ((CHAM_desc_t *) apDescExpr2)->mb; - n0 = n * ((CHAM_desc_t *) apDescExpr2)->nb; - starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), - STARPU_VALUE, &temp2, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_VALUE, &n0, sizeof(int), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr2, m, n), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr3, m, n), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr4, m, n), - STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescMLOE, m, n), - STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescMMOM, m, n), - 0); - } - } - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, this->mpContext); - return CHAMELEON_SUCCESS; -} - -template -void -LinearAlgebraMethods::CopyDescriptorZ(DescriptorData &aDescriptorData, void *apDescriptor, T *apDoubleVector) { - - // Check for initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - - RUNTIME_option_t options; - RUNTIME_options_init(&options, (chameleon_context_s *) this->mpContext, - (RUNTIME_sequence_t *) aDescriptorData.GetSequence(), - (RUNTIME_request_t *) aDescriptorData.GetRequest()); - - int m, m0; - int temp; - auto A = (CHAM_desc_t *) apDescriptor; - struct starpu_codelet *cl = &this->cl_dzcpy; - - for (m = 0; m < A->mt; m++) { - temp = m == A->mt - 1 ? A->m - m * A->mb : A->mb; - m0 = m * A->mb; - - starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_VALUE, &apDoubleVector, sizeof(double), - STARPU_W, RUNTIME_data_getaddr(A, m, 0), -#if defined(CHAMELEON_CODELETS_HAVE_NAME) - STARPU_NAME, "dzcpy", -#endif - 0); - } - RUNTIME_options_ws_free(&options); - -} - -template -int -LinearAlgebraMethods::ExaGeoStatMLEMSPEBivariateTileAsync(void *apDescZPre, void *apDescZMiss, void *apDescsError1, - void *apDescsError2, void *apDescsError, - void *apSequence, void *apRequest) { - // Check for initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - - RUNTIME_option_t options; - RUNTIME_options_init(&options, (chameleon_context_s *) this->mpContext, - (RUNTIME_sequence_t *) apSequence, - (RUNTIME_request_t *) apRequest); - - int m, m0; - int tempmm; - auto Zpre = (CHAM_desc_t *) apDescZPre; - - struct starpu_codelet *cl = &cl_dmse_bivariate; - - - for (m = 0; m < Zpre->mt; m++) { - tempmm = m == Zpre->mt - 1 ? Zpre->m - m * Zpre->mb : Zpre->mb; - m0 = m * Zpre->mb; - starpu_insert_task(cl, - STARPU_VALUE, &tempmm, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_RW, ExaGeoStatDataGetAddr(apDescsError1, 0, 0), - STARPU_RW, ExaGeoStatDataGetAddr(apDescsError2, 0, 0), - STARPU_RW, ExaGeoStatDataGetAddr(apDescsError, 0, 0), - STARPU_R, ExaGeoStatDataGetAddr(apDescZPre, m, 0), - STARPU_R, ExaGeoStatDataGetAddr(apDescZMiss, m, 0), -#if defined(CHAMELEON_CODELETS_HAVE_NAME) - STARPU_NAME, "dmse_bivariate", -#endif - 0); - } - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); - return CHAMELEON_SUCCESS; -} - #ifdef USE_HICMA template @@ -1727,4 +1421,39 @@ void LinearAlgebraMethods::ExaGeoStatPosvTile(const common::UpperLower &aUppe if (status != CHAMELEON_SUCCESS) { throw std::runtime_error("CHAMELEON_dposv_Tile Failed!"); } -} \ No newline at end of file +} + +template +bool LinearAlgebraMethods::Recover(char *apPath, const int &aIterationCount, T *apTheta, T *apLogLik, + const int &aNumParams) { + + char *pLine = nullptr, *pTokens; + size_t length = 0; + int count, i; + + FILE *pFile_handler; + pFile_handler = fopen(apPath, "r"); + + if (pFile_handler == nullptr) { + throw std::runtime_error("Cannot open observations file"); + } + while (getline(&pLine, &length, pFile_handler) != -1) { + pTokens = strtok(pLine, " "); + count = (int) strtol(pTokens, nullptr, 10); + if (count == aIterationCount) { + pTokens = strtok(nullptr, " "); + for (i = 0; i < aNumParams; i++) { + apTheta[i] = strtol(pTokens, nullptr, 10); + pTokens = strtok(nullptr, " "); + } + *apLogLik = strtol(pTokens, nullptr, 10); + fclose(pFile_handler); + free(pLine); + return true; + } + } + + fclose(pFile_handler); + free(pLine); + return false; +} diff --git a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp index 9da5263e..0b1d33f7 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp @@ -25,131 +25,19 @@ using namespace std; using namespace exageostat::common; using namespace exageostat::dataunits; +using namespace exageostat::runtime; +using namespace exageostat::results; +using namespace exageostat::configurations; using namespace exageostat::linearAlgebra; template -int ChameleonImplementation::ExaGeoStatDoubleDotProduct(void *apDescA, void *apDescProduct, void *apSequence, - void *apRequest) { - RUNTIME_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - - int m, m0; - int tempmm; - auto A = (CHAM_desc_t *) apDescA; - - struct starpu_codelet *cl=&this->cl_ddotp; - - for (m = 0; m < A->mt; m++) { - tempmm = m == A->mt-1 ? A->m - m * A->mb : A->mb; - - m0 = m * A->mb; - - - starpu_insert_task(cl, - STARPU_VALUE, &tempmm, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_RW, ExaGeoStatDataGetAddr(apDescProduct, 0, 0), - STARPU_R, RUNTIME_data_getaddr(A, m, 0), - 0); - } - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); - return CHAMELEON_SUCCESS; -} - -template -int ChameleonImplementation::ExaGeoStatNonGaussianLogLikeTileAsync(void *apDescZ, void *apDescSum, const T *apTheta, void *apSequence, void *apRequest) { - - // Check for initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - - RUNTIME_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - int m, m0; - int temp; - auto Z = (CHAM_desc_t *) apDescZ; - auto A = (CHAM_desc_t *) apDescSum; - struct starpu_codelet *cl = &this->cl_non_gaussian_loglike; - - - for (m = 0; m < Z->mt; m++) { - temp = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; - m0 = m * Z->mb; - - starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_R, ExaGeoStatDataGetAddr(Z, m, 0), - STARPU_RW, ExaGeoStatDataGetAddr(A, 0, 0), - STARPU_VALUE, &apTheta[0], sizeof(double), - STARPU_VALUE, &apTheta[1], sizeof(double), - STARPU_VALUE, &apTheta[2], sizeof(double), - STARPU_VALUE, &apTheta[3], sizeof(double), - STARPU_VALUE, &apTheta[4], sizeof(double), - STARPU_VALUE, &apTheta[5], sizeof(double), - 0); - } - - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); - return CHAMELEON_SUCCESS; -} - -template -int -ChameleonImplementation::ExaGeoStatNonGaussianTransformTileAsync(void *apDescZ, void *apDescFlag, const T *apTheta, void *apSequence, - void *apRequest) { - // Check for initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - - RUNTIME_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - int m, m0; - int tempmm; - auto Z = (CHAM_desc_t *) apDescZ; - struct starpu_codelet *cl = &this->cl_non_gaussian_transform; - - - for (m = 0; m < Z->mt; m++) { - tempmm = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; - m0 = m * Z->mb; - - starpu_insert_task(cl, - STARPU_VALUE, &tempmm, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_RW, ExaGeoStatDataGetAddr(apDescZ, m, 0), - STARPU_W, ExaGeoStatDataGetAddr(apDescFlag, 0, 0), - STARPU_VALUE, &apTheta[0], sizeof(double), - STARPU_VALUE, &apTheta[1], sizeof(double), - STARPU_VALUE, &apTheta[2], sizeof(double), - STARPU_VALUE, &apTheta[3], sizeof(double), - STARPU_VALUE, &apTheta[4], sizeof(double), - STARPU_VALUE, &apTheta[5], sizeof(double), - 0); - } - - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); - return CHAMELEON_SUCCESS; -} - -template -T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, std::unique_ptr> &aData, - Configurations &aConfigurations, const double *theta, +T ChameleonImplementation::ExaGeoStatMLETile(std::unique_ptr> &aData, + configurations::Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const kernels::Kernel &aKernel) { - this->SetContext(aHardware.GetContext(aConfigurations.GetComputation())); if (!aData->GetDescriptorData()->GetIsDescriptorInitiated()) { - this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(), aKernel.GetVariablesNumber(), apMeasurementsMatrix); + this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(), aKernel.GetVariablesNumber(), + apMeasurementsMatrix); } // Create a Chameleon sequence, if not initialized before through the same descriptors RUNTIME_request_t request_array[2] = {RUNTIME_REQUEST_INITIALIZER, RUNTIME_REQUEST_INITIALIZER}; @@ -175,42 +63,42 @@ T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardw aData->CalculateMedianLocations(kernel_name, median_locations); auto *CHAM_desc_C = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_C).chameleon_desc; + DescriptorName::DESCRIPTOR_C).chameleon_desc; auto *CHAM_desc_sub_C11 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_C11).chameleon_desc; + DescriptorName::DESCRIPTOR_C11).chameleon_desc; auto *CHAM_desc_sub_C12 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_C12).chameleon_desc; + DescriptorName::DESCRIPTOR_C12).chameleon_desc; auto *CHAM_desc_sub_C22 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_C22).chameleon_desc; + DescriptorName::DESCRIPTOR_C22).chameleon_desc; auto *CHAM_desc_Z = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z).chameleon_desc; + DescriptorName::DESCRIPTOR_Z).chameleon_desc; auto *CHAM_desc_Z1 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z_1).chameleon_desc; + DescriptorName::DESCRIPTOR_Z_1).chameleon_desc; auto *CHAM_desc_Z2 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z_2).chameleon_desc; + DescriptorName::DESCRIPTOR_Z_2).chameleon_desc; auto *CHAM_desc_Z3 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z_3).chameleon_desc; + DescriptorName::DESCRIPTOR_Z_3).chameleon_desc; auto *CHAM_desc_Zcpy = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_Z_COPY).chameleon_desc; + DescriptorName::DESCRIPTOR_Z_COPY).chameleon_desc; auto *CHAM_desc_det = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_DETERMINANT).chameleon_desc; + DescriptorName::DESCRIPTOR_DETERMINANT).chameleon_desc; auto *CHAM_desc_product = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_PRODUCT).chameleon_desc; auto *CHAM_desc_sum = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_SUM).chameleon_desc; + DescriptorName::DESCRIPTOR_SUM).chameleon_desc; auto *CHAM_desc_product1 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_PRODUCT_1).chameleon_desc; + DescriptorName::DESCRIPTOR_PRODUCT_1).chameleon_desc; auto *CHAM_desc_product2 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_PRODUCT_2).chameleon_desc; + DescriptorName::DESCRIPTOR_PRODUCT_2).chameleon_desc; auto *CHAM_desc_product3 = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, - DescriptorName::DESCRIPTOR_PRODUCT_3).chameleon_desc; + DescriptorName::DESCRIPTOR_PRODUCT_3).chameleon_desc; T *determinant = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_det); *determinant = 0; T *product = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_product); *product = 0; - T *sum ; - if(aConfigurations.GetIsNonGaussian()) { + T *sum; + if (aConfigurations.GetIsNonGaussian()) { sum = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_sum); *sum = 0; } @@ -221,21 +109,21 @@ T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardw int iter_count = aData->GetMleIterations(); if (recovery_file.empty() || - !(this->recover((char *) (recovery_file.c_str()), iter_count, (T *) theta, &loglik, num_params))) { + !(this->Recover((char *) (recovery_file.c_str()), iter_count, (T *) theta, &loglik, num_params))) { START_TIMING(dzcpy_time); if (iter_count == 0) { // Save a copy of descZ into descZcpy for restoring each iteration (Only for the first iteration) this->ExaGeoStatLapackCopyTile(EXAGEOSTAT_UPPER_LOWER, CHAM_desc_Z, CHAM_desc_Zcpy); } else { - VERBOSE("Re-store the original Z vector...") + VERBOSE("\tRe-store the original Z vector...") this->ExaGeoStatLapackCopyTile(EXAGEOSTAT_UPPER_LOWER, CHAM_desc_Zcpy, CHAM_desc_Z); - VERBOSE("Done.") + VERBOSE("\tDone.") } STOP_TIMING(dzcpy_time); } //Generate new co-variance matrix C based on new theta - VERBOSE("Generate New Covariance Matrix...") + VERBOSE("\tGenerate New Covariance Matrix...") START_TIMING(matrix_gen_time); if (kernel_name == "bivariate_matern_parsimonious2" || @@ -251,9 +139,9 @@ T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardw int distance_metric = aConfigurations.GetDistanceMetric(); - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_sub_C11, upper_lower, aData->GetLocations(), - aData->GetLocations(), &median_locations, univariate_theta, distance_metric, - &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_sub_C11, upper_lower, + aData->GetLocations(), aData->GetLocations(), &median_locations, + univariate_theta, distance_metric, &aKernel); nu12 = 0.5 * (theta[3] + theta[4]); rho = theta[5] * sqrt((tgamma(theta[3] + 1) * tgamma(theta[4] + 1)) / (tgamma(theta[3]) * tgamma(theta[4]))) * @@ -263,69 +151,76 @@ T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardw univariate2_theta[1] = theta[2]; univariate2_theta[2] = nu12; - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_sub_C12, upper_lower, &median_locations, - aData->GetLocations(), &median_locations, univariate2_theta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_sub_C12, upper_lower, + &median_locations, aData->GetLocations(), &median_locations, + univariate2_theta, 0, &aKernel); STOP_TIMING(matrix_gen_time); - VERBOSE("Done.") univariate3_theta[0] = theta[1]; univariate3_theta[1] = theta[2]; univariate3_theta[2] = theta[4]; - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_sub_C22, upper_lower, &median_locations, - aData->GetLocations(), &median_locations, univariate2_theta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_sub_C22, upper_lower, + &median_locations, aData->GetLocations(), &median_locations, + univariate2_theta, 0, &aKernel); } else { int upper_lower = EXAGEOSTAT_LOWER; - this->CovarianceMatrixCodelet(*aData->GetDescriptorData(), CHAM_desc_C, upper_lower, aData->GetLocations(), - aData->GetLocations(), &median_locations, (T *) theta, 0, &aKernel); + RuntimeFunctions::CovarianceMatrix(*aData->GetDescriptorData(), CHAM_desc_C, upper_lower, + aData->GetLocations(), aData->GetLocations(), &median_locations, + (T *) theta, 0, &aKernel); } this->ExaGeoStatSequenceWait(pSequence); STOP_TIMING(matrix_gen_time); - VERBOSE("Done.") + VERBOSE("\tDone.") - VERBOSE("Cholesky factorization of Sigma...") + VERBOSE("\tCholesky factorization of Sigma...") START_TIMING(time_facto); this->ExaGeoStatPotrfTile(EXAGEOSTAT_LOWER, CHAM_desc_C, aConfigurations.GetBand(), nullptr, nullptr, 0, 0); STOP_TIMING(time_facto); flops += flops_dpotrf(n); - VERBOSE("Done.") + VERBOSE("\tDone.") //Calculate log(|C|) --> log(square(|L|)) - VERBOSE("Calculating the log determinant ...") + VERBOSE("\tCalculating the log determinant ...") START_TIMING(logdet_calculate); - this->ExaGeoStatMeasureDetTileAsync(CHAM_desc_C, pSequence, &request_array, CHAM_desc_det); + RuntimeFunctions::ExaGeoStatMeasureDetTileAsync(aConfigurations.GetComputation(), CHAM_desc_C, pSequence, + &request_array, CHAM_desc_det); this->ExaGeoStatSequenceWait(pSequence); logdet = 2 * (*determinant); STOP_TIMING(logdet_calculate); - VERBOSE("Done.") + VERBOSE("\tDone.") - if(aConfigurations.GetIsNonGaussian()){ + if (aConfigurations.GetIsNonGaussian()) { VERBOSE("Transform Z vector to Gaussian field ...") - this->ExaGeoStatNonGaussianTransformTileAsync(CHAM_desc_Z, CHAM_desc_product, (T *) theta, pSequence, &request_array[0]); + RuntimeFunctions::ExaGeoStatNonGaussianTransformTileAsync(aConfigurations.GetComputation(), CHAM_desc_Z, + (T *) theta, pSequence, &request_array[0]); this->ExaGeoStatSequenceWait(pSequence); - VERBOSE(" Done.") + VERBOSE("\tDone.") CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Z, CHAM_desc_Z, 0, CHAM_desc_product); VERBOSE("Calculate non-Gaussian loglik ...") - this->ExaGeoStatNonGaussianLogLikeTileAsync(CHAM_desc_Z, CHAM_desc_sum, (T *) theta, pSequence, &request_array[0]); + RuntimeFunctions::ExaGeoStatNonGaussianLogLikeTileAsync(aConfigurations.GetComputation(), CHAM_desc_Z, + CHAM_desc_sum, (T *) theta, pSequence, + &request_array[0]); this->ExaGeoStatSequenceWait(pSequence); - VERBOSE(" Done.\n") + VERBOSE("\tDone.") } // Solving Linear System (L*X=Z)--->inv(L)*Z - VERBOSE("Solving the linear system ...") + VERBOSE("\tSolving the linear system ...") START_TIMING(time_solve); this->ExaGeoStatTrsmTile(EXAGEOSTAT_LEFT, EXAGEOSTAT_LOWER, EXAGEOSTAT_NO_TRANS, EXAGEOSTAT_NON_UNIT, 1, CHAM_desc_C, nullptr, nullptr, CHAM_desc_Z, 0); STOP_TIMING(time_solve); flops += flops_dtrsm(ChamLeft, n, nhrs); - VERBOSE("Done.") + VERBOSE("\tDone.") //Calculate MLE likelihood VERBOSE("Calculating the MLE likelihood function ...") - ExaGeoStatDoubleDotProduct(CHAM_desc_Z, CHAM_desc_product, pSequence, request_array); + RuntimeFunctions::ExaGeoStatDoubleDotProduct(CHAM_desc_Z, CHAM_desc_product, + pSequence, request_array); ExaGeoStatSequenceWait(pSequence); if (kernel_name == "BivariateMaternParsimonious2Profile") { @@ -339,7 +234,8 @@ T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardw } else if (kernel_name == "BivariateMaternParsimoniousProfile") { loglik = -(n / 2) + (n / 2) * log(n) - (n / 2) * log(dot_product) - 0.5 * logdet - (double) (n / 2.0) * log(2.0 * PI); - this->ExaGeoStaStrideVectorTileAsync(CHAM_desc_Z, CHAM_desc_Z1, CHAM_desc_Z2, pSequence, &request_array[0]); + RuntimeFunctions::ExaGeoStaStrideVectorTileAsync(CHAM_desc_Z, CHAM_desc_Z1, CHAM_desc_Z2, pSequence, + &request_array[0]); CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Z1, CHAM_desc_Z1, 0, CHAM_desc_product1); CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Z2, CHAM_desc_Z2, 0, CHAM_desc_product2); variance1 = (1.0 / (n / 2)) * dot_product1; @@ -349,8 +245,8 @@ T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardw loglik = -(n / 3.0) + (n / 3.0) * log(n / 3.0) - (n / 3.0) * log(dot_product) - 0.5 * logdet - (double) (n / 3.0) * log(2.0 * PI); //to be optimized - this->ExaGeoStaStrideVectorTileAsync(CHAM_desc_Z, CHAM_desc_Z1, CHAM_desc_Z2, CHAM_desc_Z3, pSequence, - &request_array[0]); + RuntimeFunctions::ExaGeoStaStrideVectorTileAsync(CHAM_desc_Z, CHAM_desc_Z1, CHAM_desc_Z2, CHAM_desc_Z3, + pSequence, &request_array[0]); CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Z1, CHAM_desc_Z1, 0, CHAM_desc_product1); CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Z2, CHAM_desc_Z2, 0, CHAM_desc_product2); CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Z3, CHAM_desc_Z3, 0, CHAM_desc_product3); @@ -359,23 +255,23 @@ T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardw } else { dot_product = *product; loglik = -0.5 * dot_product - 0.5 * logdet; - if(aConfigurations.GetIsNonGaussian()){ + if (aConfigurations.GetIsNonGaussian()) { loglik = loglik - *sum - n * log(theta[3]) - (double) (n / 2.0) * log(2.0 * PI); } else { loglik = loglik - (double) (n / 2.0) * log(2.0 * PI); } } - VERBOSE("Done.") + VERBOSE("\tDone.") - //Distribute the values in the case of MPI + //Distribute the values in the case of MPI #ifdef USE_MPI - MPI_Bcast(&loglik, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD ); + MPI_Bcast(&loglik, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD ); #endif - LOGGER(iter_count + 1 << " - Model Parameters (", true) + LOGGER("\t" << iter_count + 1 << " - Model Parameters (", true) if (aConfigurations.GetLogger()) { - fprintf(aConfigurations.GetFileLogPath(), " %3d- Model Parameters (", iter_count + 1); + fprintf(aConfigurations.GetFileLogPath(), "\t %d- Model Parameters (", iter_count + 1); } if ((aConfigurations.GetKernelName() == "BivariateMaternParsimoniousProfile") || @@ -403,33 +299,33 @@ T ChameleonImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardw if (aConfigurations.GetLogger()) { fprintf(aConfigurations.GetFileLogPath(), ")----> LogLi: %.18f\n", loglik); } - LOGGER(" ---- Facto Time: " << time_facto) - LOGGER(" ---- Log Determent Time: " << logdet_calculate) - LOGGER(" ---- dtrsm Time: " << time_solve) - LOGGER(" ---- Matrix Generation Time: " << matrix_gen_time) - LOGGER(" ---- Total Time: " << time_facto + logdet_calculate + time_solve) - LOGGER(" ---- Gflop/s: " << flops / 1e9 / (time_facto + time_solve)) + VERBOSE("---- Facto Time: " << time_facto) + VERBOSE("---- Log Determent Time: " << logdet_calculate) + VERBOSE("---- dtrsm Time: " << time_solve) + VERBOSE("---- Matrix Generation Time: " << matrix_gen_time) + VERBOSE("---- Total Time: " << time_facto + logdet_calculate + time_solve) + VERBOSE("---- Gflop/s: " << flops / 1e9 / (time_facto + time_solve)) aData->SetMleIterations(aData->GetMleIterations() + 1); // for experiments and benchmarking accumulated_executed_time = - results::Results::GetInstance()->GetTotalModelingExecutionTime() + time_facto + logdet_calculate + + Results::GetInstance()->GetTotalModelingExecutionTime() + time_facto + logdet_calculate + time_solve; - results::Results::GetInstance()->SetTotalModelingExecutionTime(accumulated_executed_time); + Results::GetInstance()->SetTotalModelingExecutionTime(accumulated_executed_time); accumulated_flops = - results::Results::GetInstance()->GetTotalModelingFlops() + (flops / 1e9 / (time_facto + time_solve)); - results::Results::GetInstance()->SetTotalModelingFlops(accumulated_flops); + Results::GetInstance()->GetTotalModelingFlops() + (flops / 1e9 / (time_facto + time_solve)); + Results::GetInstance()->SetTotalModelingFlops(accumulated_flops); - results::Results::GetInstance()->SetMLEIterations(iter_count + 1); - results::Results::GetInstance()->SetMaximumTheta(vector(theta, theta + num_params)); - results::Results::GetInstance()->SetLogLikValue(loglik); + Results::GetInstance()->SetMLEIterations(iter_count + 1); + Results::GetInstance()->SetMaximumTheta(vector(theta, theta + num_params)); + Results::GetInstance()->SetLogLikValue(loglik); return loglik; } template -void ChameleonImplementation::ExaGeoStatLapackCopyTile(const common::UpperLower &aUpperLower, void *apA, void *apB) { +void ChameleonImplementation::ExaGeoStatLapackCopyTile(const UpperLower &aUpperLower, void *apA, void *apB) { int status = CHAMELEON_dlacpy_Tile((cham_uplo_t) aUpperLower, (CHAM_desc_t *) apA, (CHAM_desc_t *) apB); if (status != CHAMELEON_SUCCESS) { throw std::runtime_error("CHAMELEON_dlacpy_Tile Failed!"); @@ -437,13 +333,8 @@ void ChameleonImplementation::ExaGeoStatLapackCopyTile(const common::UpperLow } template -void *ChameleonImplementation::ExaGeoStatDataGetAddr(void *apA, int aAm, int aAn) { - return RUNTIME_data_getaddr((CHAM_desc_t *) apA, aAm, aAn); -} - -template -void ChameleonImplementation::ExaGeoStatTrsmTile(const common::Side &aSide, const common::UpperLower &aUpperLower, - const common::Trans &aTrans, const common::Diag &aDiag, +void ChameleonImplementation::ExaGeoStatTrsmTile(const Side &aSide, const UpperLower &aUpperLower, + const Trans &aTrans, const Diag &aDiag, const T &aAlpha, void *apA, void *apCD, void *apCrk, void *apZ, const int &aMaxRank) { int status = CHAMELEON_dtrsm_Tile((cham_side_t) aSide, (cham_uplo_t) aUpperLower, (cham_trans_t) aTrans, @@ -468,137 +359,3 @@ void ChameleonImplementation::ExaGeoStatCreateSequence(void *apSequence) { throw std::runtime_error("CHAMELEON_Sequence_Create Failed!"); } } - -template -void -ChameleonImplementation::ExaGeoStatOptionsInit(void *apOptions, void *apContext, void *apSequence, void *apRequest) { - RUNTIME_options_init((RUNTIME_option_t *) apOptions, (CHAM_context_t *) apContext, - (RUNTIME_sequence_t *) apSequence, (RUNTIME_request_t *) apRequest); -} - -template -void ChameleonImplementation::ExaGeoStatOptionsFree(void *apOptions) { - RUNTIME_options_ws_free((RUNTIME_option_t *) apOptions); -} - -template -void ChameleonImplementation::ExaGeoStatOptionsFinalize(void *apOptions, void *apContext) { - RUNTIME_options_finalize((RUNTIME_option_t *) apOptions, (CHAM_context_t *) apContext); -} - -template -int ChameleonImplementation::ExaGeoStatMeasureDetTileAsync(void *apDescA, void *apSequence, void *apRequest, - void *apDescDet) { - - // Check for initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - RUNTIME_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - int m; - int temp; - auto Z = (CHAM_desc_t *) apDescA; - auto det = (CHAM_desc_t *) apDescDet; - struct starpu_codelet *cl = &this->cl_dmdet; - - for (m = 0; m < Z->mt; m++) { - temp = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; - starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), - STARPU_R, ExaGeoStatDataGetAddr(Z, m, m), - STARPU_RW, ExaGeoStatDataGetAddr(det, 0, 0), - 0); - } - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); - return CHAMELEON_SUCCESS; -} - -template -int ChameleonImplementation::ExaGeoStaStrideVectorTileAsync(void *apDescA, void *apDescB, void *apDescC, - void *apSequence, void *apRequest) { - - // Check for initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - - RUNTIME_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - int m, m0; - int temp; - auto A = (CHAM_desc_t *) apDescA; - auto B = (CHAM_desc_t *) apDescB; - auto C = (CHAM_desc_t *) apDescC; - struct starpu_codelet *cl = &this->cl_stride_vec; - - for (m = 0; m < A->mt; m++) { - temp = m == A->mt - 1 ? A->m - m * A->mb : A->mb; - m0 = m * A->mb; - starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_VALUE, &m, sizeof(int), - STARPU_R, ExaGeoStatDataGetAddr(A, m, 0), - STARPU_W, ExaGeoStatDataGetAddr(B, (int) floor(m / 2.0), 0), - STARPU_W, ExaGeoStatDataGetAddr(C, (int) floor(m / 2.0), 0), -#if defined(CHAMELEON_CODELETS_HAVE_NAME) - STARPU_NAME, "stride_vec", -#endif - 0); - - } - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); - return CHAMELEON_SUCCESS; -} - -template -int -ChameleonImplementation::ExaGeoStaStrideVectorTileAsync(void *apDescA, void *apDescB, void *apDescC, void *apDescD, - void *apSequence, void *apRequest) { - - // Check for initialize the Chameleon context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - - RUNTIME_option_t options; - RUNTIME_options_init(&options, (CHAM_context_t *) this->mpContext, (RUNTIME_sequence_t *) apSequence, - (RUNTIME_request_t *) apRequest); - - int m, m0; - int temp; - auto A = (CHAM_desc_t *) apDescA; - auto B = (CHAM_desc_t *) apDescB; - auto C = (CHAM_desc_t *) apDescC; - auto D = (CHAM_desc_t *) apDescD; - struct starpu_codelet *cl = &this->cl_tri_stride_vec; - - for (m = 0; m < A->mt; m++) { - temp = m == A->mt - 1 ? A->m - m * A->mb : A->mb; - m0 = m * A->mb; - starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_VALUE, &m, sizeof(int), - STARPU_R, ExaGeoStatDataGetAddr(A, m, 0), - STARPU_W, ExaGeoStatDataGetAddr(B, (int) floor(m / 3.0), 0), - STARPU_W, ExaGeoStatDataGetAddr(C, (int) floor(m / 3.0), 0), - STARPU_W, ExaGeoStatDataGetAddr(D, (int) floor(m / 3.0), 0), -#if defined(CHAMELEON_CODELETS_HAVE_NAME) - STARPU_NAME, "tristride_vec", -#endif - 0); - - } - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, (CHAM_context_t *) this->mpContext); - return CHAMELEON_SUCCESS; -} \ No newline at end of file diff --git a/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp b/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp index f29bc027..47eca4e8 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp @@ -12,6 +12,8 @@ * @date 2023-03-20 **/ +#include + #include using namespace std; @@ -26,4 +28,6 @@ ChameleonDense::ExaGeoStatPotrfTile(const common::UpperLower &aUpperLower, vo if (status != CHAMELEON_SUCCESS) { throw std::runtime_error("CHAMELEON_dpotrf_Tile Failed, Matrix is not positive definite"); } + // Due to a leak in dense mode in Chameleon, We had to free the buffer manually. + mkl_free_buffers(); } \ No newline at end of file diff --git a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp index d1974a8c..4c0d0a87 100644 --- a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp @@ -22,9 +22,11 @@ using namespace std; using namespace exageostat::linearAlgebra::tileLowRank; using namespace exageostat::common; +using namespace exageostat::runtime; using namespace exageostat::dataunits; using namespace exageostat::kernels; using namespace exageostat::helpers; +using namespace exageostat::results; int store_only_diagonal_tiles = 1; int use_scratch = 1; @@ -32,7 +34,7 @@ int global_check = 0; //used to create dense matrix for accuracy check template void HicmaImplementation::SetModelingDescriptors(std::unique_ptr> &aData, - Configurations &aConfigurations, const int &aP) { + configurations::Configurations &aConfigurations, const int &aP) { int full_problem_size = aConfigurations.GetProblemSize() * aP; int lts = aConfigurations.GetLowTileSize(); @@ -83,10 +85,12 @@ void HicmaImplementation::SetModelingDescriptors(std::unique_ptrGetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_Z, is_OOC, nullptr, float_point, - lts, lts, lts * lts, full_problem_size, 1, 0, 0, full_problem_size, 1, p_grid, q_grid); + lts, lts, lts * lts, full_problem_size, 1, 0, 0, full_problem_size, 1, + p_grid, q_grid); aData->GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_Z_COPY, is_OOC, nullptr, float_point, - lts, lts, lts * lts, full_problem_size, 1, 0, 0, full_problem_size, 1, p_grid, q_grid); + lts, lts, lts * lts, full_problem_size, 1, 0, 0, full_problem_size, 1, + p_grid, q_grid); if (aConfigurations.GetIsNonGaussian()) { aData->GetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_PRODUCT, is_OOC, nullptr, float_point, @@ -98,14 +102,13 @@ void HicmaImplementation::SetModelingDescriptors(std::unique_ptr -T HicmaImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, - Configurations &aConfigurations, const double *theta, +T HicmaImplementation::ExaGeoStatMLETile(std::unique_ptr> &aData, + configurations::Configurations &aConfigurations, const double *theta, T *apMeasurementsMatrix, const Kernel &aKernel) { - this->SetContext(ExaGeoStatHardware::GetContext(aConfigurations.GetComputation())); if (!aData->GetDescriptorData()->GetIsDescriptorInitiated()) { - this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(),aKernel.GetVariablesNumber(), apMeasurementsMatrix); + this->InitiateDescriptors(aConfigurations, *aData->GetDescriptorData(), aKernel.GetVariablesNumber(), + apMeasurementsMatrix); } // Create a Hicma sequence, if not initialized before through the same descriptors RUNTIME_request_t request_array[2] = {HICMA_REQUEST_INITIALIZER, HICMA_REQUEST_INITIALIZER}; @@ -171,7 +174,7 @@ T HicmaImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, string recovery_file = aConfigurations.GetRecoveryFile(); if (recovery_file.empty() || - !(this->recover((char *) (recovery_file.c_str()), iter_count, (T *) theta, &loglik, num_params))) { + !(this->Recover((char *) (recovery_file.c_str()), iter_count, (T *) theta, &loglik, num_params))) { if (iter_count == 0) { // Copy dense matrix Z into tile low rank Z. this->CopyDescriptors(CHAM_descZ, HICMA_descZ, N, CHAMELEON_TO_HICMA); @@ -229,17 +232,17 @@ T HicmaImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, //Calculate log(|C|) --> log(square(|L|)) VERBOSE("LR:Calculating the log determinant ...") START_TIMING(logdet_calculate); - ExaGeoStatMeasureDetTileAsync(HICMA_descCD, pSequence, &request_array[0], HICMA_desc_det); + RuntimeFunctions::ExaGeoStatMeasureDetTileAsync(aConfigurations.GetComputation(), HICMA_descCD, pSequence, + &request_array[0], HICMA_desc_det); ExaGeoStatSequenceWait(pSequence); - logdet = 2 * (*determinant); STOP_TIMING(logdet_calculate); VERBOSE("Done.") if (aConfigurations.GetIsNonGaussian()) { VERBOSE("Transform Z vector to Gaussian field ...") - this->ExaGeoStatNonGaussianTransformTileAsync(HICMA_descZ, HICMA_desc_product, (T *) theta, pSequence, - &request_array[0]); + RuntimeFunctions::ExaGeoStatNonGaussianTransformTileAsync(aConfigurations.GetComputation(), HICMA_descZ, + (T *) theta, pSequence, &request_array[0]); ExaGeoStatSequenceWait(pSequence); VERBOSE(" Done.") @@ -257,8 +260,9 @@ T HicmaImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, #else global_sum = *product; #endif - this->ExaGeoStatNonGaussianLogLikeTileAsync(HICMA_descZ, HICMA_desc_sum, (T *) theta, pSequence, - &request_array[0]); + RuntimeFunctions::ExaGeoStatNonGaussianLogLikeTileAsync(aConfigurations.GetComputation(), HICMA_descZ, + HICMA_desc_sum, (T *) theta, pSequence, + &request_array[0]); ExaGeoStatSequenceWait(pSequence); VERBOSE(" Done.") } @@ -287,9 +291,9 @@ T HicmaImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, } VERBOSE("Done.") - LOGGER(iter_count + 1 << " - Model Parameters (", true) + LOGGER("\t" << iter_count + 1 << " - Model Parameters (", true) if (aConfigurations.GetLogger()) { - fprintf(aConfigurations.GetFileLogPath(), " %3d- Model Parameters (", iter_count + 1); + fprintf(aConfigurations.GetFileLogPath(), "\t %d- Model Parameters (", iter_count + 1); } if ((aConfigurations.GetKernelName() == "bivariate_matern_parsimonious_profile") || (aConfigurations.GetKernelName() == "bivariate_matern_parsimonious2_profile")) { @@ -317,27 +321,27 @@ T HicmaImplementation::ExaGeoStatMLETile(const ExaGeoStatHardware &aHardware, fprintf(aConfigurations.GetFileLogPath(), ")----> LogLi: %.18f\n", loglik); } - LOGGER(" ---- Facto Time: " << time_facto) - LOGGER(" ---- Log Determent Time: " << logdet_calculate) - LOGGER(" ---- dtrsm Time: " << time_solve) - LOGGER(" ---- Matrix Generation Time: " << matrix_gen_time) - LOGGER(" ---- Total Time: " << time_facto + logdet_calculate + time_solve) - LOGGER(" ---- Gflop/s: " << flops / 1e9 / (time_facto + time_solve)) + VERBOSE(" ---- Facto Time: " << time_facto) + VERBOSE(" ---- Log Determent Time: " << logdet_calculate) + VERBOSE(" ---- dtrsm Time: " << time_solve) + VERBOSE(" ---- Matrix Generation Time: " << matrix_gen_time) + VERBOSE(" ---- Total Time: " << time_facto + logdet_calculate + time_solve) + VERBOSE(" ---- Gflop/s: " << flops / 1e9 / (time_facto + time_solve)) aData->SetMleIterations(aData->GetMleIterations() + 1); // for experiments and benchmarking accumulated_executed_time = - results::Results::GetInstance()->GetTotalModelingExecutionTime() + time_facto + logdet_calculate + + Results::GetInstance()->GetTotalModelingExecutionTime() + time_facto + logdet_calculate + time_solve; - results::Results::GetInstance()->SetTotalModelingExecutionTime(accumulated_executed_time); + Results::GetInstance()->SetTotalModelingExecutionTime(accumulated_executed_time); accumulated_flops = - results::Results::GetInstance()->GetTotalModelingFlops() + (flops / 1e9 / (time_facto + time_solve)); - results::Results::GetInstance()->SetTotalModelingFlops(accumulated_flops); + Results::GetInstance()->GetTotalModelingFlops() + (flops / 1e9 / (time_facto + time_solve)); + Results::GetInstance()->SetTotalModelingFlops(accumulated_flops); - results::Results::GetInstance()->SetMLEIterations(iter_count + 1); - results::Results::GetInstance()->SetMaximumTheta(vector(theta, theta + num_params)); - results::Results::GetInstance()->SetLogLikValue(loglik); + Results::GetInstance()->SetMLEIterations(iter_count + 1); + Results::GetInstance()->SetMaximumTheta(vector(theta, theta + num_params)); + Results::GetInstance()->SetLogLikValue(loglik); aConfigurations.SetEstimatedTheta(aConfigurations.GetStartingTheta()); return loglik; @@ -352,19 +356,6 @@ void HicmaImplementation::ExaGeoStatLapackCopyTile(const UpperLower &aUpperLo } } -template -void -HicmaImplementation::ExaGeoStatOptionsInit(void *apOptions, void *apContext, void *apSequence, void *apRequest) { - HICMA_RUNTIME_options_init((HICMA_option_t *) apOptions, (HICMA_context_t *) apContext, - (HICMA_sequence_t *) apSequence, (HICMA_request_t *) apRequest); -} - -template -void HicmaImplementation::ExaGeoStatOptionsFree(void *apOptions) { - HICMA_RUNTIME_options_ws_free((HICMA_option_t *) apOptions); - -} - template void HicmaImplementation::ExaGeoStatSequenceWait(void *apSequence) { HICMA_Sequence_Wait((HICMA_sequence_t *) apSequence); @@ -378,12 +369,6 @@ void HicmaImplementation::ExaGeoStatCreateSequence(void *apSequence) { } } -template -void HicmaImplementation::ExaGeoStatOptionsFinalize(void *apOptions, void *apContext) { - RUNTIME_options_finalize((RUNTIME_option_t *) apOptions, (CHAM_context_t *) apContext); - -} - template void HicmaImplementation::ExaGeoStatPotrfTile(const common::UpperLower &aUpperLower, void *apA, int aBand, void *apCD, void *apCrk, const int &aMaxRank, const int &aAcc) { @@ -406,131 +391,3 @@ void HicmaImplementation::ExaGeoStatTrsmTile(const common::Side &aSide, const throw std::runtime_error("HICMA_dtrsmd_Tile Failed!"); } } - -template -int HicmaImplementation::ExaGeoStatMeasureDetTileAsync(void *apDescA, void *apSequence, void *apRequest, - void *apDescDet) { - - // Check for initialize the Hicma context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - HICMA_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - int m; - int temp; - auto Z = (HICMA_desc_t *) apDescA; - auto det = (HICMA_desc_t *) apDescDet; - struct starpu_codelet *cl = &this->cl_dmdet; - - for (m = 0; m < Z->mt; m++) { - temp = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; - starpu_insert_task(cl, - STARPU_VALUE, &temp, sizeof(int), - STARPU_R, ExaGeoStatDataGetAddr(Z, m, 0), - STARPU_RW, ExaGeoStatDataGetAddr(det, 0, 0), - 0); - } - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, (HICMA_context_t *) - this->mpContext); - return HICMA_SUCCESS; -} - -template -void *HicmaImplementation::ExaGeoStatDataGetAddr(void *apA, int aAm, int aAn) { - return HICMA_RUNTIME_data_getaddr((HICMA_desc_t *) apA, aAm, aAn); - -} - -template -int -HicmaImplementation::ExaGeoStatNonGaussianLogLikeTileAsync(void *apDescZ, void *apDescSum, const T *apTheta, - void *apSequence, - void *apRequest) { - // Check for initialize the Hicma context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - HICMA_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - int m, m0; - int tempmm; - auto Z = (HICMA_desc_t *) apDescZ; - - struct starpu_codelet *cl = &this->cl_non_gaussian_loglike_lr; - - - for (m = 0; m < Z->mt; m++) { - tempmm = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; - - m0 = m * Z->mb; - - starpu_insert_task(cl, - STARPU_VALUE, &tempmm, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_R, ExaGeoStatDataGetAddr(apDescZ, m, 0), - STARPU_RW, ExaGeoStatDataGetAddr(apDescSum, 0, 0), - STARPU_VALUE, &apTheta[0], sizeof(double), - STARPU_VALUE, &apTheta[1], sizeof(double), - STARPU_VALUE, &apTheta[2], sizeof(double), - STARPU_VALUE, &apTheta[3], sizeof(double), - STARPU_VALUE, &apTheta[4], sizeof(double), - STARPU_VALUE, &apTheta[5], sizeof(double), -#if defined(CHAMELEON_CODELETS_HAVE_NAME) - STARPU_NAME, "non_gaussian_loglike_lr", -#endif - 0); - } - - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, (HICMA_context_t *) - this->mpContext); - return HICMA_SUCCESS; -} - - -template -int HicmaImplementation::ExaGeoStatNonGaussianTransformTileAsync(void *apDescZ, void *apDescFlag, const T *apTheta, - void *apSequence, void *apRequest) { - // Check for initialize the Hicma context. - if (!this->mpContext) { - throw std::runtime_error( - "ExaGeoStat hardware is not initialized, please use 'ExaGeoStatHardware(computation, cores_number, gpu_numbers);'."); - } - HICMA_option_t options; - this->ExaGeoStatOptionsInit(&options, this->mpContext, apSequence, apRequest); - - int m, m0; - int tempmm; - auto Z = (HICMA_desc_t *) apDescZ; - struct starpu_codelet *cl = &this->cl_non_gaussian_transform_lr; - - - for (m = 0; m < Z->mt; m++) { - tempmm = m == Z->mt - 1 ? Z->m - m * Z->mb : Z->mb; - m0 = m * Z->mb; - starpu_insert_task(cl, - STARPU_VALUE, &tempmm, sizeof(int), - STARPU_VALUE, &m0, sizeof(int), - STARPU_RW, ExaGeoStatDataGetAddr(apDescZ, m, 0), - STARPU_VALUE, &apTheta[0], sizeof(double), - STARPU_VALUE, &apTheta[1], sizeof(double), - STARPU_VALUE, &apTheta[2], sizeof(double), - STARPU_VALUE, &apTheta[3], sizeof(double), - STARPU_VALUE, &apTheta[4], sizeof(double), - STARPU_VALUE, &apTheta[5], sizeof(double), -#if defined(CHAMELEON_CODELETS_HAVE_NAME) - STARPU_NAME, "non_gaussian_transform_lr", -#endif - 0); - } - this->ExaGeoStatOptionsFree(&options); - this->ExaGeoStatOptionsFinalize(&options, (HICMA_context_t *) - this->mpContext); - return HICMA_SUCCESS; -} diff --git a/src/prediction/Prediction.cpp b/src/prediction/Prediction.cpp index 4a955d96..813ccdb6 100644 --- a/src/prediction/Prediction.cpp +++ b/src/prediction/Prediction.cpp @@ -12,19 +12,24 @@ * @date 2024-02-04 **/ +#include + #include #include #include #include +using namespace std; + using namespace exageostat::prediction; using namespace exageostat::dataunits; +using namespace exageostat::results; +using namespace exageostat::configurations; template -void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, - std::unique_ptr> &aData, - Configurations &aConfigurations, T *apMeasurementsMatrix, - const kernels::Kernel &aKernel) { +void Prediction::PredictMissingData(unique_ptr> &aData, Configurations &aConfigurations, + T *apMeasurementsMatrix, const kernels::Kernel &aKernel, + Locations *apTrainLocations, Locations *apTestLocations) { int i, j; bool can_predict = true; @@ -38,37 +43,59 @@ void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, if (!can_predict && (aConfigurations.GetIsMLOEMMOM() || aConfigurations.GetIsMSPE() || aConfigurations.GetIsFisher())) { - throw std::runtime_error( + throw runtime_error( "Can't predict without an estimated theta, please either pass --etheta or run the modeling module before prediction"); } int number_of_mspe = 3; int p = aKernel.GetVariablesNumber(); - int z_miss_number = aConfigurations.GetUnknownObservationsNb(); - int n_z_obs = aConfigurations.CalculateZObsNumber(); + int z_miss_number, n_z_obs; + T *z_actual; + if(!apTrainLocations && !apTestLocations){ + z_miss_number = aConfigurations.GetUnknownObservationsNb(); + n_z_obs = aConfigurations.CalculateZObsNumber(); + z_actual = new T[z_miss_number * p]; + } + else{ + z_miss_number = apTestLocations->GetSize(); + n_z_obs = apTrainLocations->GetSize(); + z_actual = nullptr; + } + aConfigurations.SetObservationNumber(n_z_obs); auto linear_algebra_solver = linearAlgebra::LinearAlgebraFactory::CreateLinearAlgebraSolver(common::EXACT_DENSE); + VERBOSE("\t- Total number of Z: " << aConfigurations.GetProblemSize()) + LOGGER("\t- Number of Z Miss: " << z_miss_number) + LOGGER("\t- Number of Z observations: " << n_z_obs) + // FISHER Prediction Function Call if (aConfigurations.GetIsFisher()) { - LOGGER("---- Using Prediction Function Fisher ----") + LOGGER("\t---- Using Prediction Function Fisher ----") T *fisher_results; - fisher_results = linear_algebra_solver->ExaGeoStatFisherTile(aConfigurations, aData, aHardware, + fisher_results = linear_algebra_solver->ExaGeoStatFisherTile(aConfigurations, aData, (T *) aConfigurations.GetEstimatedTheta().data(), aKernel); - - LOGGER("- Sd of sigma2, alpha, nu: " << sqrt(fisher_results[0]) << " " << sqrt(fisher_results[4]) << " " - << sqrt(fisher_results[8])) - LOGGER("- CI for sigma2: " << aConfigurations.GetEstimatedTheta()[0] - Q_NORM * sqrt(fisher_results[0]) << " " - << aConfigurations.GetEstimatedTheta()[0] + Q_NORM * sqrt(fisher_results[0])) - - LOGGER("- CI for alpha: " << aConfigurations.GetEstimatedTheta()[1] - Q_NORM * sqrt(fisher_results[4]) << " " - << aConfigurations.GetEstimatedTheta()[1] + Q_NORM * sqrt(fisher_results[4])) - LOGGER("- CI for nu: " << aConfigurations.GetEstimatedTheta()[2] - Q_NORM * sqrt(fisher_results[8]) << " " - << aConfigurations.GetEstimatedTheta()[2] + Q_NORM * sqrt(fisher_results[8])) - - LOGGER("- Fisher Matrix:") + vector fisher_vector; + fisher_vector.reserve(num_params * num_params); // Reserve memory in advance for efficiency + for (size_t idx = 0; idx < num_params * num_params; ++idx) { + fisher_vector.push_back(fisher_results[idx]); + } + Results::GetInstance()->SetFisherMatrix(fisher_vector); + VERBOSE("\t\t- Sd of sigma2, alpha, nu: " << sqrt(fisher_results[0]) << " " << sqrt(fisher_results[4]) << " " + << sqrt(fisher_results[8])) + VERBOSE("\t\t- CI for sigma2: " << aConfigurations.GetEstimatedTheta()[0] - Q_NORM * sqrt(fisher_results[0]) + << " " + << aConfigurations.GetEstimatedTheta()[0] + Q_NORM * sqrt(fisher_results[0])) + + VERBOSE("\t\t- CI for alpha: " << aConfigurations.GetEstimatedTheta()[1] - Q_NORM * sqrt(fisher_results[4]) + << " " + << aConfigurations.GetEstimatedTheta()[1] + Q_NORM * sqrt(fisher_results[4])) + VERBOSE("\t\t- CI for nu: " << aConfigurations.GetEstimatedTheta()[2] - Q_NORM * sqrt(fisher_results[8]) << " " + << aConfigurations.GetEstimatedTheta()[2] + Q_NORM * sqrt(fisher_results[8])) + + LOGGER("\t\t- Fisher Matrix:") for (i = 0; i < num_params; i++) { - LOGGER(" ", true) + LOGGER("\t\t ", true) for (j = 0; j < num_params; j++) { LOGGER_PRECISION(fisher_results[i * num_params + j], 18) if (j != num_params - 1) { @@ -77,7 +104,6 @@ void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, } LOGGER("") } - LOGGER("") delete[] fisher_results; } @@ -85,27 +111,24 @@ void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, return; } - LOGGER("- Total number of Z: " << aConfigurations.GetProblemSize()) - LOGGER("- Number of Z Miss: " << z_miss_number) - LOGGER("- Number of Z observations: " << n_z_obs) - - results::Results::GetInstance()->SetZMiss(z_miss_number); + Results::GetInstance()->SetZMiss(z_miss_number); + aConfigurations.SetUnknownObservationsNb(z_miss_number); T *z_obs = new T[n_z_obs * p]; T *z_miss = new T[z_miss_number]; - T *z_actual = new T[z_miss_number * p]; - std::vector avg_pred_value(number_of_mspe); + + vector avg_pred_value(number_of_mspe); auto miss_locations = new Locations(z_miss_number, aConfigurations.GetDimension()); // Prediction is only supported with 2D. auto obs_locations = new Locations(n_z_obs, aConfigurations.GetDimension()); // We Predict date with only Exact computation. This is a pre-request. InitializePredictionArguments(aConfigurations, aData, linear_algebra_solver, z_obs, z_actual, *miss_locations, - *obs_locations, apMeasurementsMatrix, p); + *obs_locations, apMeasurementsMatrix, p, apTrainLocations, apTestLocations); // MLOE MMOM Auxiliary Function Call if (aConfigurations.GetIsMLOEMMOM()) { LOGGER("---- Using Auxiliary Function MLOE MMOM ----") - linear_algebra_solver->ExaGeoStatMLETileMLOEMMOM(aConfigurations, aData, aHardware, + linear_algebra_solver->ExaGeoStatMLETileMLOEMMOM(aConfigurations, aData, (T *) aConfigurations.GetInitialTheta().data(), (T *) aConfigurations.GetEstimatedTheta().data(), *miss_locations, *obs_locations, aKernel); @@ -113,36 +136,36 @@ void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, // IDW Auxiliary Function Call if (aConfigurations.GetIsIDW()) { - LOGGER("---- Using Auxiliary Function IDW ----") + LOGGER("\t---- Using Auxiliary Function IDW ----") T *mspe = new T[number_of_mspe]; + + if(!z_actual){ + z_actual = new T[z_miss_number * p]; + memcpy(z_actual, apMeasurementsMatrix + n_z_obs, z_miss_number * sizeof(T)); + } + PredictionAuxiliaryFunctions::PredictIDW(z_miss, z_actual, z_obs, z_miss_number, n_z_obs, *miss_locations, *obs_locations, mspe); - LOGGER("- Average prediction Error (IDW): ", true) - for (i = 0; i < number_of_mspe; i++) { - avg_pred_value[i] += mspe[i]; - LOGGER_PRECISION(avg_pred_value[i], 9) - if (i != number_of_mspe - 1) { - LOGGER_PRECISION(", ") - } + vector idw_error; + idw_error.reserve(number_of_mspe); // Reserve memory in advance for efficiency + for (size_t idx = 0; idx < number_of_mspe; ++idx) { + idw_error.push_back(mspe[idx]); } - results::Results::GetInstance()->SetIDWError( - std::vector(avg_pred_value.data(), avg_pred_value.data() + number_of_mspe)); - LOGGER("") + + Results::GetInstance()->SetIDWError(idw_error); delete[] mspe; } // MSPE Prediction Function Call if (aConfigurations.GetIsMSPE()) { - LOGGER("---- Using MSPE ----") + LOGGER("\t---- Using MSPE ----") T *prediction_error_mspe; if (aConfigurations.GetIsNonGaussian()) { prediction_error_mspe = linear_algebra_solver->ExaGeoStatMLENonGaussianPredictTile(aData, (T *) aConfigurations.GetEstimatedTheta().data(), z_miss_number, n_z_obs, - z_obs, - z_actual, z_miss, - aHardware, + z_obs, z_actual, z_miss, aConfigurations, *miss_locations, *obs_locations, aKernel); @@ -150,19 +173,22 @@ void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, prediction_error_mspe = linear_algebra_solver->ExaGeoStatMLEPredictTile(aData, (T *) aConfigurations.GetEstimatedTheta().data(), z_miss_number, n_z_obs, z_obs, - z_actual, z_miss, aHardware, - aConfigurations, *miss_locations, - *obs_locations, aKernel); + z_actual, z_miss, aConfigurations, + *miss_locations, *obs_locations, + aKernel); } for (i = 0; i < number_of_mspe; i++) { avg_pred_value[i] += prediction_error_mspe[i]; } - LOGGER("- MSPE: " << avg_pred_value[0]) - LOGGER("- Average prediction Error (mspe): ", true) - for (i = 0; i < number_of_mspe; i++) { - LOGGER_PRECISION(avg_pred_value[i] << "\t", 8) + vector z_miss_vector; + z_miss_vector.reserve(z_miss_number); // Reserve memory in advance for efficiency + for (size_t idx = 0; idx < z_miss_number; ++idx) { + z_miss_vector.push_back(z_miss[idx]); + } + Results::GetInstance()->SetPredictedMissedValues(z_miss_vector); + if(z_actual){ + LOGGER("\t\t- MSPE: " << avg_pred_value[0]) } - LOGGER("") delete[] prediction_error_mspe; } @@ -174,18 +200,31 @@ void Prediction::PredictMissingData(const ExaGeoStatHardware &aHardware, } template -void Prediction::InitializePredictionArguments(Configurations &aConfigurations, - std::unique_ptr> &aData, - std::unique_ptr> &aLinearAlgebraSolver, +void Prediction::InitializePredictionArguments(Configurations &aConfigurations, unique_ptr> &aData, + unique_ptr> &aLinearAlgebraSolver, T *apZObs, T *apZActual, Locations &aMissLocation, - Locations &aObsLocation, T *apMeasurementsMatrix, const int &aP) { + Locations &aObsLocation, T *apMeasurementsMatrix, const int &aP, + Locations *apTrainLocations, Locations *apTestLocations) { int full_problem_size = aConfigurations.GetProblemSize() * aP; T *z = new T[full_problem_size]; aLinearAlgebraSolver->ExaGeoStatGetZObs(aConfigurations, z, full_problem_size, *aData->GetDescriptorData(), apMeasurementsMatrix, aP); - PredictionHelpers::PickRandomPoints(aConfigurations, aData, apZObs, apZActual, z, aMissLocation, aObsLocation, - aP); + + if(!apTrainLocations && !apTestLocations){ + PredictionHelpers::PickRandomPoints(aConfigurations, aData, apZObs, apZActual, z, aMissLocation, aObsLocation, aP); + } + else{ + for (int i = 0; i < apTrainLocations->GetSize(); ++i) { + aObsLocation.GetLocationX()[i] = apTrainLocations->GetLocationX()[i]; + aObsLocation.GetLocationY()[i] = apTrainLocations->GetLocationY()[i]; + } + for (int i = 0; i < apTestLocations->GetSize(); ++i) { + aMissLocation.GetLocationX()[i] = apTestLocations->GetLocationX()[i]; + aMissLocation.GetLocationY()[i] = apTestLocations->GetLocationY()[i]; + } + memcpy(apZObs, apMeasurementsMatrix, aObsLocation.GetSize() * sizeof(T)); + } delete[] z; } \ No newline at end of file diff --git a/src/prediction/PredictionAuxiliaryFunctions.cpp b/src/prediction/PredictionAuxiliaryFunctions.cpp index 52fecd42..e02466a9 100644 --- a/src/prediction/PredictionAuxiliaryFunctions.cpp +++ b/src/prediction/PredictionAuxiliaryFunctions.cpp @@ -61,5 +61,5 @@ void PredictionAuxiliaryFunctions::PredictIDW(T *apZMiss, T *apZActual, T *ap for (int index = 0; index < aZMissNumber; index++) { VERBOSE(" (" << apZActual[index] << ", " << apZMiss[index] << ")") } - LOGGER("- Prediction Error (IDW): " << apMSPE[0] << " - " << apMSPE[1] << " - " << apMSPE[2]) + LOGGER("\t\t- Prediction Error (IDW): " << apMSPE[0] << " - " << apMSPE[1] << " - " << apMSPE[2]) } \ No newline at end of file diff --git a/src/prediction/PredictionHelpers.cpp b/src/prediction/PredictionHelpers.cpp index d0f0f88e..2e547678 100644 --- a/src/prediction/PredictionHelpers.cpp +++ b/src/prediction/PredictionHelpers.cpp @@ -17,6 +17,7 @@ #include using namespace exageostat::prediction; +using namespace exageostat::configurations; using namespace exageostat::dataunits; template diff --git a/src/results/Results.cpp b/src/results/Results.cpp index d93d6ae7..69cfc6ad 100644 --- a/src/results/Results.cpp +++ b/src/results/Results.cpp @@ -13,9 +13,13 @@ #include #include +#include using namespace exageostat::results; using namespace exageostat::common; +using namespace exageostat::configurations; + +using namespace std; Results *Results::GetInstance() { @@ -37,7 +41,7 @@ void Results::SetIsLogger(bool aIsLogger) { this->mIsLogger = aIsLogger; } -void Results::SetLoggerPath(const std::string &aLoggerPath) { +void Results::SetLoggerPath(const string &aLoggerPath) { this->mLoggerPath = aLoggerPath; } @@ -45,18 +49,11 @@ void Results::PrintEndSummary() { Verbose temp = Configurations::GetVerbosity(); Configurations::SetVerbosity(STANDARD_MODE); - LOGGER("") LOGGER("********************SUMMARY**********************") auto locations_number = this->mGeneratedLocationsNumber; if (locations_number > 0) { - LOGGER("---- Data Generation Results ----") - if (this->mIsSynthetic) { - LOGGER(" #Synthetic Dataset") - } else { - LOGGER(" #Real Dataset") - } - LOGGER(" #Number of Locations: " << locations_number) + LOGGER("#Number of Locations: " << locations_number) if (this->mIsLogger && this->mIsSynthetic) { LOGGER(" #Data is written to file (", true) if (this->mLoggerPath.empty()) { @@ -65,39 +62,33 @@ void Results::PrintEndSummary() { LOGGER_PRECISION(this->mLoggerPath << ").") LOGGER("") } - LOGGER(" #Total Data Generation Execution Time: " << this->mExecutionTimeDataGeneration) - LOGGER(" #Total Data Generation Gflop/s: " << this->mFlopsDataGeneration) - LOGGER("") + VERBOSE("#Total Data Generation Execution Time: " << this->mExecutionTimeDataGeneration) + VERBOSE("#Total Data Generation Gflop/s: " << this->mFlopsDataGeneration) } if (this->mMLEIterations > 0) { - LOGGER("---- Data Modeling Results ----") - LOGGER(" #Number of MLE Iterations till reach Maximum: " << this->mMLEIterations) - LOGGER(" #Found Maximum Theta at: ", true) + LOGGER("#Number of MLE Iterations: " << this->mMLEIterations) + LOGGER("#Found Maximum Theta at: ", true) for (double i: this->mMaximumTheta) { LOGGER_PRECISION(i << " ", 8) } LOGGER("") - LOGGER(" #Final Log Likelihood value: " << this->mLogLikValue) - LOGGER(" #Average Time Modeling per Iteration: " << this->GetAverageModelingExecutionTime()) - LOGGER(" #Average Flops per Iteration: " << this->GetAverageModelingFlops()) - LOGGER(" #Total MLE Execution time: " << this->mTotalModelingExecutionTime) - LOGGER(" #Total MLE Gflop/s: " << this->mTotalModelingFlops) - LOGGER("") + LOGGER("#Final Log Likelihood value: " << this->mLogLikValue) + VERBOSE("#Average Time Modeling per Iteration: " << this->GetAverageModelingExecutionTime()) + VERBOSE("#Average Flops per Iteration: " << this->GetAverageModelingFlops()) + VERBOSE("#Total MLE Execution time: " << this->mTotalModelingExecutionTime) + VERBOSE("#Total MLE GFlop/s: " << this->mTotalModelingFlops) } if (this->mZMiss > 0) { - LOGGER("---- Data Prediction Results ----") - LOGGER(" #Number of Missing Observations: " << this->mZMiss) + LOGGER("#Number of Missing Observations: " << this->mZMiss) if (this->mMSPEError > 0) { - LOGGER(" #MSPE") - LOGGER(" #MSPE Prediction Execution Time: " << this->mExecutionTimeMSPE) - LOGGER(" #MSPE Gflop/s: " << this->mFlopsMSPE) - LOGGER(" #Mean Square Error MSPE: " << this->mMSPEError) + VERBOSE("#MSPE Prediction Execution Time: " << this->mExecutionTimeMSPE) + VERBOSE("#MSPE Gflop/s: " << this->mFlopsMSPE) + LOGGER("#Mean Square Error MSPE: " << this->mMSPEError) } if (!this->mIDWError.empty()) { - LOGGER(" #IDW") - LOGGER(" #IDW Error: ( ", true) + LOGGER("#IDW Error: ( ", true) for (int i = 0; i < 3; i++) { LOGGER_PRECISION(this->mIDWError[i] << " ", 8) } @@ -105,24 +96,19 @@ void Results::PrintEndSummary() { LOGGER("") } if (this->mMLOE > 0 || this->mMMOM > 0) { - LOGGER(" #MLOE MMOM") - LOGGER(" #MLOE: " << this->mMLOE) - LOGGER(" #MMOM: " << this->mMMOM) - LOGGER(" #MLOE-MMOM Execution Time: " << this->mExecutionTimeMLOEMMOM) - LOGGER(" #MLOE-MMOM Matrix Generation Time: " << this->mGenerationTimeMLOEMMOM) - LOGGER(" #MLOE-MMOM Cholesky Factorization Time: " << this->mFactoTimeMLOEMMOM) - LOGGER(" #MLOE-MMOM Loop Time: " << this->mLoopTimeMLOEMMOM) - LOGGER(" #MLOE-MMOM Number of flops: " << this->mFlopsMLOEMMOM) - LOGGER("") + LOGGER("#MLOE: " << this->mMLOE << "\t\t#MMOM: " << this->mMMOM) + VERBOSE("#MLOE-MMOM Execution Time: " << this->mExecutionTimeMLOEMMOM) + VERBOSE("#MLOE-MMOM Matrix Generation Time: " << this->mGenerationTimeMLOEMMOM) + VERBOSE("#MLOE-MMOM Cholesky Factorization Time: " << this->mFactoTimeMLOEMMOM) + VERBOSE("#MLOE-MMOM Loop Time: " << this->mLoopTimeMLOEMMOM) + VERBOSE("#MLOE-MMOM Number of flops: " << this->mFlopsMLOEMMOM) } } - if (this->mFisher00 != 0) { - LOGGER(" #Fisher") - LOGGER(" #Sd For Sigma2: " << this->mFisher00) - LOGGER(" #Sd For Alpha: " << this->mFisher11) - LOGGER(" #Sd For Nu: " << this->mFisher22) - LOGGER(" #Fisher Execution Time: " << this->mTotalFisherTime) - LOGGER("") + if (!this->mFisherMatrix.empty()) { + LOGGER("#Sd For Sigma2: " << this->mFisherMatrix[0]) + LOGGER("#Sd For Alpha: " << this->mFisherMatrix[1]) + LOGGER("#Sd For Nu: " << this->mFisherMatrix[2]) + VERBOSE("#Fisher Execution Time: " << this->mTotalFisherTime) } LOGGER("*************************************************") Configurations::SetVerbosity(temp); @@ -132,7 +118,7 @@ void Results::SetMLEIterations(int aIterationsNumber) { this->mMLEIterations = aIterationsNumber; } -void Results::SetMaximumTheta(const std::vector &aMaximumTheta) { +void Results::SetMaximumTheta(const vector &aMaximumTheta) { this->mMaximumTheta = aMaximumTheta; } @@ -148,7 +134,7 @@ void Results::SetMSPEError(double aMSPEError) { this->mMSPEError = aMSPEError; } -void Results::SetIDWError(const std::vector &aIDWError) { +void Results::SetIDWError(const vector &aIDWError) { this->mIDWError = aIDWError; } @@ -180,14 +166,14 @@ double Results::GetAverageModelingExecutionTime() const { if (this->mMLEIterations) { return this->mTotalModelingExecutionTime / this->mMLEIterations; } - throw std::runtime_error("Number of MLE Iterations is not set!"); + throw runtime_error("Number of MLE Iterations is not set!"); } double Results::GetAverageModelingFlops() const { if (this->mMLEIterations) { return this->mTotalModelingFlops / this->mMLEIterations; } - throw std::runtime_error("Number of MLE Iterations is not set!"); + throw runtime_error("Number of MLE Iterations is not set!"); } void Results::SetTotalModelingFlops(double aTime) { @@ -232,19 +218,14 @@ void Results::SetTotalFisherTime(double aTime) { this->mTotalFisherTime = aTime; } -void Results::SetFisher00(double aFisher00) { - this->mFisher00 = aFisher00; -} - -void Results::SetFisher11(double aFisher11) { - this->mFisher11 = aFisher11; +void Results::SetFisherMatrix(vector aFisherMatrix) { + this->mFisherMatrix = std::move(aFisherMatrix); } -void Results::SetFisher22(double aFisher22) { - this->mFisher22 = aFisher22; +void Results::SetPredictedMissedValues(vector aPredictedValues) { + this->mPredictedMissedValues = std::move(aPredictedValues); } - double Results::GetMLOE() const { return this->mMLOE; } @@ -253,7 +234,7 @@ double Results::GetMSPEError() const { return this->mMSPEError; } -std::vector Results::GetIDWError() const { +vector Results::GetIDWError() const { return this->mIDWError; } @@ -261,16 +242,10 @@ double Results::GetMMOM() const { return this->mMMOM; } -double Results::GetFisher00() const { - return this->mFisher00; -} - - -double Results::GetFisher11() const { - return this->mFisher11; -} - -double Results::GetFisher22() const { - return this->mFisher22; +std::vector Results::GetFisherMatrix() const { + return this->mFisherMatrix; } +std::vector Results::GetPredictedMissedValues() const { + return this->mPredictedMissedValues; +} \ No newline at end of file diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt new file mode 100644 index 00000000..dfcb8f10 --- /dev/null +++ b/src/runtime/CMakeLists.txt @@ -0,0 +1,25 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @brief CMake build script for StarPu functions +# @author Mahmoud ElKarargy +# @date 2024-03-10 + +# Include runtime directory,based on runtime flag. +if ("${RUNTIME_TYPE}" STREQUAL "parsec") + add_subdirectory(parsec) +else () + #by default use StarPu runtime. + add_subdirectory(starpu) +endif () + +# Define the sources for the library +set(SOURCES + ${SOURCES} + PARENT_SCOPE +) + diff --git a/src/runtime/parsec/CMakeLists.txt b/src/runtime/parsec/CMakeLists.txt new file mode 100644 index 00000000..904c01c6 --- /dev/null +++ b/src/runtime/parsec/CMakeLists.txt @@ -0,0 +1,17 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @brief CMake build script for Parsec runtime +# @author Mahmoud ElKarargy +# @date 2024-03-10 + +# Define the sources for the library +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/ParsecFunctions.cpp + ${SOURCES} + PARENT_SCOPE +) \ No newline at end of file diff --git a/src/runtime/parsec/ParsecFunctions.cpp b/src/runtime/parsec/ParsecFunctions.cpp new file mode 100644 index 00000000..0e5a2e29 --- /dev/null +++ b/src/runtime/parsec/ParsecFunctions.cpp @@ -0,0 +1,87 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ParsecFunctions.cpp + * @brief A class for static functions used in Parsec runtime + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-03-10 +**/ + +#include + +using namespace std; +using namespace exageostat::common; +using namespace exageostat::runtime; +using namespace exageostat::dataunits; + +//TODO: implement parsec functions +template +void RuntimeFunctions::CovarianceMatrix(DescriptorData &aDescriptorData, void *apDescriptor, + const int &aTriangularPart, Locations *apLocation1, + Locations *apLocation2, Locations *apLocation3, + T *apLocalTheta, const int &aDistanceMetric, + const kernels::Kernel *apKernel, void *apContext) {} + +template +void RuntimeFunctions::ExaGeoStatMLETileAsyncMLOEMMOM(void *apDescExpr1, void *apDescExpr2, void *apDescExpr3, + void *apDescMLOE, void *apDescMMOM, void *apSequence, + void *apRequest, void *apContext) {} + +template +void RuntimeFunctions::ExaGeoStatMLEMSPETileAsync(void *apDescZPredict, void *apDescZMiss, void *apDescError, + void *apSequence, void *apRequest, void *apContext) {} + +template +void RuntimeFunctions::CopyDescriptorZ(DescriptorData &aDescriptorData, void *apDescriptor, T *apDoubleVector, + void *apContext) {} + +template +void +RuntimeFunctions::ExaGeoStatGaussianToNonTileAsync(DescriptorData &aDescriptorData, void *apDesc, T *apTheta, + void *apContext) {} + +template +void +RuntimeFunctions::ExaGeoStatMeasureDetTileAsync(const Computation &aComputation, void *apDescA, void *apSequence, + void *apRequest, void *apDescDet, void *apContext) {} + +template +void RuntimeFunctions::ExaGeoStaStrideVectorTileAsync(void *apDescA, void *apDescB, + void *apDescC, void *apSequence, void *apRequest, + void *apContext) {} + +template +void RuntimeFunctions::ExaGeoStaStrideVectorTileAsync(void *apDescA, void *apDescB, void *apDescC, void *apDescD, + void *apSequence, void *apRequest, void *apContext) {} + +template +void +RuntimeFunctions::ExaGeoStatMLETraceTileAsync(void *apDescA, void *apSequence, void *apRequest, void *apDescNum, + void *apDescTrace, void *apContext) {} + +template +void +RuntimeFunctions::ExaGeoStatDoubleDotProduct(void *apDescA, void *apDescProduct, void *apSequence, + void *apRequest, + void *apContext) {} + +template +void +RuntimeFunctions::ExaGeoStatMLEMSPEBivariateTileAsync(void *apDescZPre, void *apDescZMiss, void *apDescsError1, + void *apDescsError2, void *apDescsError, void *apSequence, + void *apRequest, void *apContext) {} + +template +void RuntimeFunctions::ExaGeoStatNonGaussianLogLikeTileAsync(const Computation &aComputation, void *apDescZ, + void *apDescSum, const T *apTheta, void *apSequence, + void *apRequest, void *apContext) {} + +template +void +RuntimeFunctions::ExaGeoStatNonGaussianTransformTileAsync(const Computation &aComputation, void *apDescZ, + const T *apTheta, + void *apSequence, void *apRequest, void *apContext) {} diff --git a/src/runtime/starpu/CMakeLists.txt b/src/runtime/starpu/CMakeLists.txt new file mode 100644 index 00000000..23944963 --- /dev/null +++ b/src/runtime/starpu/CMakeLists.txt @@ -0,0 +1,55 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @brief CMake build script for StarPu runtime +# @author Mahmoud ElKarargy +# @date 2024-02-19 + +# Include the concrete implementations of the StarPu codelets classes +file(GLOB ALL_CODELETS ${CMAKE_CURRENT_SOURCE_DIR}/concrete/*.cpp) + +# Include StarPu helpers. +add_subdirectory(helpers) + +# Define the sources for the library +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/StarPuFunctions.cpp + ${ALL_CODELETS} + ${SOURCES} + PARENT_SCOPE +) + +# Automatically add new codelets header files to StarPuCodeletsHeaders.hpp + +# File documentation +set(DOCUMENTATION_STRING " +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file StarPuCodelets.hpp + * @brief Header file to include all codelet classes. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-25 +**/ + +") + +set(PATH_TO_CODELETS_HEADERS "${PROJECT_SOURCE_DIR}/inst/include/runtime/starpu") +set(GENERATED_INCLUDES "${PATH_TO_CODELETS_HEADERS}/StarPuCodeletsHeaders.hpp") + +file(GLOB_RECURSE HEADER_FILES "${PATH_TO_CODELETS_HEADERS}/concrete/*.hpp") +file(WRITE ${GENERATED_INCLUDES} ${DOCUMENTATION_STRING}) + +foreach(HEADER_FILE ${HEADER_FILES}) + # Construct the include directive by stripping the known base directory part from the full path + string(REPLACE "${PATH_TO_CODELETS_HEADERS}" "" HEADER_RELATIVE_PATH ${HEADER_FILE}) + # Use angle brackets and the desired base path for the include directive + file(APPEND ${GENERATED_INCLUDES} "#include \n") +endforeach() diff --git a/src/runtime/starpu/StarPuFunctions.cpp b/src/runtime/starpu/StarPuFunctions.cpp new file mode 100644 index 00000000..9ecbf050 --- /dev/null +++ b/src/runtime/starpu/StarPuFunctions.cpp @@ -0,0 +1,259 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file StarPuFunctions.cpp + * @brief A class for static functions that make use of starpu_codelets. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-28 +**/ + +#include +#include +#include +#include + +using namespace std; +using namespace exageostat::common; +using namespace exageostat::runtime; +using namespace exageostat::dataunits; + +template +void RuntimeFunctions::CovarianceMatrix(dataunits::DescriptorData &aDescriptorData, void *apDescriptor, + const int &aTriangularPart, + dataunits::Locations *apLocation1, dataunits::Locations *apLocation2, + dataunits::Locations *apLocation3, T *apLocalTheta, + const int &aDistanceMetric, + const kernels::Kernel *apKernel) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(EXACT_DENSE); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, aDescriptorData.GetSequence(), + aDescriptorData.GetRequest()); + + DCMGCodelet cl; + cl.InsertTask(apDescriptor, aTriangularPart, apLocation1, apLocation2, apLocation3, apLocalTheta, aDistanceMetric, + apKernel); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void RuntimeFunctions::ExaGeoStatMLETileAsyncMLOEMMOM(void *apDescExpr1, void *apDescExpr2, void *apDescExpr3, + void *apDescMLOE, + void *apDescMMOM, void *apSequence, void *apRequest) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(EXACT_DENSE); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, apSequence, apRequest); + + DmloeMmomCodelet cl; + cl.InsertTask(apDescExpr1, apDescExpr2, apDescExpr3, apDescMLOE, apDescMMOM); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void RuntimeFunctions::ExaGeoStatMLEMSPETileAsync(void *apDescZPredict, void *apDescZMiss, void *apDescError, + void *apSequence, + void *apRequest) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(EXACT_DENSE); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, apSequence, apRequest); + + DMSECodelet cl; + cl.InsertTask(apDescError, apDescZPredict, apDescZMiss); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void RuntimeFunctions::CopyDescriptorZ(dataunits::DescriptorData &aDescriptorData, void *apDescriptor, + T *apDoubleVector) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(EXACT_DENSE); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, aDescriptorData.GetSequence(), + aDescriptorData.GetRequest()); + + DZCPYCodelet cl; + cl.InsertTask(apDescriptor, apDoubleVector); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void RuntimeFunctions::ExaGeoStatGaussianToNonTileAsync(dataunits::DescriptorData &aDescriptorData, void *apDesc, + T *apTheta) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(EXACT_DENSE); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, aDescriptorData.GetSequence(), + aDescriptorData.GetRequest()); + + GaussianCodelet cl; + cl.InsertTask(apDesc, apTheta); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void +RuntimeFunctions::ExaGeoStatMeasureDetTileAsync(const common::Computation &aComputation, void *apDescA, + void *apSequence, void *apRequest, + void *apDescDet) { + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(aComputation); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, apSequence, apRequest); + + DMDETCodelet cl; + cl.InsertTask(aComputation, apDescA, apDescDet, starpu_helper); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void RuntimeFunctions::ExaGeoStaStrideVectorTileAsync(void *apDescA, void *apDescB, void *apDescC, void *apSequence, + void *apRequest) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(EXACT_DENSE); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, apSequence, apRequest); + + STRIDEVECCodelet cl; + cl.InsertTask(apDescA, apDescB, apDescC); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void RuntimeFunctions::ExaGeoStaStrideVectorTileAsync(void *apDescA, void *apDescB, void *apDescC, void *apDescD, + void *apSequence, + void *apRequest) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(EXACT_DENSE); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, apSequence, apRequest); + + TriStrideVecCodelet cl; + cl.InsertTask(apDescA, apDescB, apDescC, apDescD); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void RuntimeFunctions::ExaGeoStatMLETraceTileAsync(void *apDescA, void *apSequence, void *apRequest, void *apDescNum, + void *apDescTrace) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(EXACT_DENSE); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, apSequence, apRequest); + + DTRACECodelet cl; + cl.InsertTask(apDescA, apDescNum, apDescTrace); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void +RuntimeFunctions::ExaGeoStatDoubleDotProduct(void *apDescA, void *apDescProduct, void *apSequence, void *apRequest) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(EXACT_DENSE); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, apSequence, apRequest); + + DDOTPCodelet cl; + cl.InsertTask(apDescA, apDescProduct); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void RuntimeFunctions::ExaGeoStatMLEMSPEBivariateTileAsync(void *apDescZPre, void *apDescZMiss, void *apDescError1, + void *apDescError2, + void *apDescError, void *apSequence, void *apRequest) { + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(EXACT_DENSE); + auto *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, apSequence, apRequest); + + DMSEBivariateCodelet cl; + cl.InsertTask(apDescZPre, apDescError, apDescError1, apDescError2, apDescZMiss); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void RuntimeFunctions::ExaGeoStatNonGaussianLogLikeTileAsync(const common::Computation &aComputation, void *apDescZ, + void *apDescSum, + const T *apTheta, void *apSequence, void *apRequest) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(aComputation); + void *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, apSequence, apRequest); + + NonGaussianLoglike cl; + cl.InsertTask(apDescZ, apDescSum, apTheta, starpu_helper); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} + +template +void +RuntimeFunctions::ExaGeoStatNonGaussianTransformTileAsync(const common::Computation &aComputation, void *apDescZ, + const T *apTheta, + void *apSequence, void *apRequest) { + + auto starpu_helper = StarPuHelpersFactory::CreateStarPuHelper(aComputation); + void *pOptions = starpu_helper->GetOptions(); + starpu_helper->ExaGeoStatOptionsInit(pOptions, apSequence, apRequest); + + NonGaussianTransform cl; + cl.InsertTask(apDescZ, apTheta, starpu_helper); + + starpu_helper->ExaGeoStatOptionsFree(pOptions); + starpu_helper->ExaGeoStatOptionsFinalize(pOptions); + starpu_helper->DeleteOptions(pOptions); + +} diff --git a/src/runtime/starpu/concrete/dcmg-codelet.cpp b/src/runtime/starpu/concrete/dcmg-codelet.cpp new file mode 100644 index 00000000..3ed51f80 --- /dev/null +++ b/src/runtime/starpu/concrete/dcmg-codelet.cpp @@ -0,0 +1,88 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dcmg-codelet.cpp + * @brief A class for starpu codelet dcmg. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-19 +**/ + +#include +#include + +using namespace exageostat::runtime; +using namespace exageostat::dataunits; +using namespace exageostat::kernels; + +template +struct starpu_codelet DCMGCodelet::cl_dcmg = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_dcmg_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_dcmg_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 1, + .modes = {STARPU_W}, + .name = "dcmg" +}; + +template +void DCMGCodelet::InsertTask(void *apDescriptor, const int &aTriangularPart, Locations *apLocation1, + Locations *apLocation2, Locations *apLocation3, T *apLocalTheta, + const int &aDistanceMetric, const Kernel *apKernel) { + int rows_num, cols_num, row, col, tile_row = 0, tile_col = 0; + auto *CHAM_apDescriptor = (CHAM_desc_t *) apDescriptor; + + for (col = 0; col < CHAM_apDescriptor->nt; col++) { + cols_num = col == CHAM_apDescriptor->nt - 1 ? CHAM_apDescriptor->n - col * CHAM_apDescriptor->nb + : CHAM_apDescriptor->nb; + if (aTriangularPart == ChamUpperLower) { + row = 0; + } else { + row = CHAM_apDescriptor->m == CHAM_apDescriptor->n ? col : 0; + } + for (; row < CHAM_apDescriptor->mt; row++) { + rows_num = row == CHAM_apDescriptor->mt - 1 ? CHAM_apDescriptor->m - row * CHAM_apDescriptor->mb + : CHAM_apDescriptor->mb; + tile_row = row * CHAM_apDescriptor->mb; + tile_col = col * CHAM_apDescriptor->nb; + starpu_insert_task(&this->cl_dcmg, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_VALUE, &cols_num, sizeof(int), + STARPU_VALUE, &tile_row, sizeof(int), + STARPU_VALUE, &tile_col, sizeof(int), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr(CHAM_apDescriptor, row, col), + STARPU_VALUE, &apLocation1, sizeof(Locations *), + STARPU_VALUE, &apLocation2, sizeof(Locations *), + STARPU_VALUE, &apLocation3, sizeof(Locations *), + STARPU_VALUE, &apLocalTheta, sizeof(double *), + STARPU_VALUE, &aDistanceMetric, sizeof(int), + STARPU_VALUE, &apKernel, sizeof(kernels::Kernel *), + 0); + } + } +} + +template +void DCMGCodelet::cl_dcmg_function(void *apBuffers[], void *apCodeletArguments) { + int rows_num, cols_num, tile_row, tile_col, distance_metric; + Locations *pLocation1, *pLocation2, *pLocation3; + T *pLocal_theta, *pDescriptor_A; + Kernel *pKernel; + + pDescriptor_A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + starpu_codelet_unpack_args(apCodeletArguments, &rows_num, &cols_num, &tile_row, &tile_col, &pLocation1, &pLocation2, &pLocation3, &pLocal_theta, + &distance_metric, &pKernel); + pKernel->GenerateCovarianceMatrix(pDescriptor_A, rows_num, cols_num, tile_row, tile_col, *pLocation1, *pLocation2, *pLocation3, pLocal_theta, distance_metric); +} diff --git a/src/runtime/starpu/concrete/ddotp-codelet.cpp b/src/runtime/starpu/concrete/ddotp-codelet.cpp new file mode 100644 index 00000000..b8047a28 --- /dev/null +++ b/src/runtime/starpu/concrete/ddotp-codelet.cpp @@ -0,0 +1,72 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ddotp-codelet.cpp + * @brief A class for starpu codelet ddotp. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#include + +#include +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet DDOTPCodelet::cl_ddotp = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_ddotp_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_ddotp_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 2, + .modes = {STARPU_RW, STARPU_R}, + .name = "ddotp" +}; + +template +void DDOTPCodelet::InsertTask(void *apDescA, void *apDescProduct) { + + int row, rows_num; + auto pDesc_A = (CHAM_desc_t *) apDescA; + auto pDesc_product = (CHAM_desc_t *) apDescProduct; + + auto desc_mt = pDesc_A->mt; + auto desc_m = pDesc_A->m; + auto desc_mb = pDesc_A->mb; + + for ( row = 0; row < desc_mt; row++) { + rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; + + starpu_insert_task(&this->cl_ddotp, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_RW,(starpu_data_handle_t) RUNTIME_data_getaddr(pDesc_product, 0, 0), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr(pDesc_A, row, 0), + 0); + } +} + +template +void DDOTPCodelet::cl_ddotp_function(void *apBuffers[], void *apCodeletArguments) { + int rows_num; + T *pDescriptor_A, *pDot_product; + + pDot_product = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pDescriptor_A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + starpu_codelet_unpack_args(apCodeletArguments, &rows_num); + T local_dot = cblas_ddot(rows_num, (double *) pDescriptor_A, 1, (double *) pDescriptor_A, 1); + *pDot_product += local_dot; +} diff --git a/src/runtime/starpu/concrete/dmdet-codelet.cpp b/src/runtime/starpu/concrete/dmdet-codelet.cpp new file mode 100644 index 00000000..25a0648c --- /dev/null +++ b/src/runtime/starpu/concrete/dmdet-codelet.cpp @@ -0,0 +1,90 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dmdet-codelet.cpp + * @brief A class for starpu codelet dmdet. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-21 +**/ + +#include + +#include + +using namespace exageostat::runtime; +using namespace exageostat::common; + +template +struct starpu_codelet DMDETCodelet::cl_dmdet{ +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_dmdet_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_dmdet_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 2, + .modes = {STARPU_R, STARPU_RW}, + .name = "dmdet" +}; + +template +void DMDETCodelet::InsertTask(const Computation &aComputation, void *apDescA, void *apDescDet, + std::unique_ptr &aStarPuHelpers) { + int row, rows_num; + auto desc_mt = aStarPuHelpers->GetMT(apDescA); + auto desc_m = aStarPuHelpers->GetM(apDescA); + auto desc_mb = aStarPuHelpers->GetMB(apDescA); + + if (aComputation == DIAGONAL_APPROX || aComputation == EXACT_DENSE) { + for (row = 0; row < desc_mt; row++) { + rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; + starpu_insert_task(&this->cl_dmdet, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_R, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescA, row, row), + STARPU_RW, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescDet, 0, 0), + 0); + } + } else if (aComputation == TILE_LOW_RANK) { + for (row = 0; row < desc_mt; row++) { + rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; + starpu_insert_task(&this->cl_dmdet, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_R, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescA, row, 0), + STARPU_RW, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescDet, 0, 0), + 0); + } + } +} + +template +void DMDETCodelet::cl_dmdet_function(void *apBuffers[], void *apCodeletArguments) { + int rows_num; + T *pDescriptor_A, *pDeterminant ; + + pDescriptor_A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pDeterminant = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + + starpu_codelet_unpack_args(apCodeletArguments, &rows_num); + T local_det = core_dmdet(pDescriptor_A, rows_num); + *pDeterminant += local_det; +} + +template +T DMDETCodelet::core_dmdet(const T *apDescriptor, const int &aSize) { + T result = 0.0; + for (int i = 0; i < aSize; i++) { + if (apDescriptor[i + i * aSize] > 0) + result += log(apDescriptor[i + i * aSize]); + } + return result; +} diff --git a/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp b/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp new file mode 100644 index 00000000..5409c8a8 --- /dev/null +++ b/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp @@ -0,0 +1,98 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dmloe-mmom-codelet.cpp + * @brief A class for starpu codelet dmloe-mmom. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#include + +#include +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet DmloeMmomCodelet::cl_dmloe_mmom = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_dmloe_mmom_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_dmloe_mmom_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 5, + .modes = {STARPU_R, STARPU_R, STARPU_R, STARPU_RW, STARPU_RW}, + .name = "dmloe_mmom" +}; + +template +void DmloeMmomCodelet::InsertTask(void *apDescExpr1, void *apDescExpr2, void *apDescExpr3, void *apDescMLOE, + void *apDescMMOM) { + int row, col, rows_num, cols_num; + + for (col = 0; col < ((CHAM_desc_t *) apDescExpr1)->nt; col++) { + cols_num = col == ((CHAM_desc_t *) apDescExpr1)->nt - 1 ? ((CHAM_desc_t *) apDescExpr1)->n - + col * ((CHAM_desc_t *) apDescExpr1)->nb + : ((CHAM_desc_t *) apDescExpr1)->nb; + for (row = 0; row < ((CHAM_desc_t *) apDescExpr1)->mt; row++) { + + rows_num = row == ((CHAM_desc_t *) apDescExpr1)->mt - 1 ? ((CHAM_desc_t *) apDescExpr1)->m - + row * ((CHAM_desc_t *) apDescExpr1)->mb + : ((CHAM_desc_t *) apDescExpr1)->mb; + starpu_insert_task(&this->cl_dmloe_mmom, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_VALUE, &cols_num, sizeof(int), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr1, row, col), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr2, row, col), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr3, row, col), + STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescMLOE, row, col), + STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescMMOM, row, col), + 0); + } + } +} + +template +void DmloeMmomCodelet::cl_dmloe_mmom_function(void **apBuffers, void *apCodeletArguments) { + int rows_num, cols_num; + T *pExpr1, *pExpr2, *pExpr3, *pMloe, *pMmom; + + pExpr1 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pExpr2 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pExpr3 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + pMloe = (T *) STARPU_MATRIX_GET_PTR(apBuffers[3]); + pMmom = (T *) STARPU_MATRIX_GET_PTR(apBuffers[4]); + + starpu_codelet_unpack_args(apCodeletArguments, &rows_num, &cols_num); + T expr1_ = 0, expr2_ = 0, expr3_ = 0; + + for (int i = 0; i < rows_num * cols_num; i += 2) { + expr1_ += pExpr1[i]; + expr2_ += pExpr2[i]; + expr3_ += pExpr3[i]; + } + + if (expr2_ == 0.0) { + *pMloe -= 1.0; + } else { + *pMloe += (expr1_ / expr2_) - 1.0; + } + + if (expr2_ == 0.0) { + *pMmom -= 1.0; + } else { + *pMmom += (expr3_ / expr1_) - 1.0; + } +} diff --git a/src/runtime/starpu/concrete/dmse-bivariate-codelet.cpp b/src/runtime/starpu/concrete/dmse-bivariate-codelet.cpp new file mode 100644 index 00000000..978dc1bb --- /dev/null +++ b/src/runtime/starpu/concrete/dmse-bivariate-codelet.cpp @@ -0,0 +1,87 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dmse-bivariate-codelet.cpp + * @brief A class for starpu codelet dmse-bivariate. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#include + +#include +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet DMSEBivariateCodelet::cl_dmse_bivariate = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_dmse_bivariate_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_dmse_bivariate_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 5, + .modes = {STARPU_RW, STARPU_RW, STARPU_RW, STARPU_R, STARPU_R}, + .name = "dmse-bivariate" +}; + +template +void DMSEBivariateCodelet::InsertTask(void *apDescZMiss, void *apDescZPre, void *apDescsError, void *apDescsError1, + void *apDescsError2) { + int row, rows_num; + + auto pDesc_ZPre = (CHAM_desc_t *) apDescZPre; + auto desc_mt = pDesc_ZPre->mt; + auto desc_m = pDesc_ZPre->m; + auto desc_mb = pDesc_ZPre->mb; + + for (row = 0; row < desc_mt; row++) { + rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; + starpu_insert_task(&this->cl_dmse_bivariate, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescsError1, 0, 0), + STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescsError2, 0, 0), + STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescsError, 0, 0), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZPre, row, 0), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZMiss, row, 0), + 0); + } +} + +template +void DMSEBivariateCodelet::cl_dmse_bivariate_function(void **apBuffers, void *apCodeletArguments) { + int rows_num; + T *pZpre, *pZmiss, *pSerror1, *pSerror2, *pSerror; + T local_serror1 = 0.0, local_serror2 = 0.0, local_serror = 0.0; + + pSerror1 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pSerror2 = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pSerror = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + pZpre = (T *) STARPU_MATRIX_GET_PTR(apBuffers[3]); + pZmiss = (T *) STARPU_MATRIX_GET_PTR(apBuffers[4]); + + starpu_codelet_unpack_args(apCodeletArguments, &rows_num); + + for (int i = 0; i < rows_num; i++) { + if (i % 2 == 0) { + local_serror1 += pow((pZpre[i] - pZmiss[i]), 2); + } else + local_serror2 += pow((pZpre[i] - pZmiss[i]), 2); + local_serror += pow((pZpre[i] - pZmiss[i]), 2); + } + *pSerror1 += local_serror1; + *pSerror2 += local_serror2; + *pSerror += local_serror; +} diff --git a/src/runtime/starpu/concrete/dmse-codelet.cpp b/src/runtime/starpu/concrete/dmse-codelet.cpp new file mode 100644 index 00000000..6240fe80 --- /dev/null +++ b/src/runtime/starpu/concrete/dmse-codelet.cpp @@ -0,0 +1,71 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dmse-codelet.cpp + * @brief A class for starpu codelet dmse. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-21 +**/ + +#include + +#include +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet DMSECodelet::cl_dmse = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_dmse_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_dmse_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 3, + .modes = {STARPU_RW, STARPU_R, STARPU_R}, + .name = "dmse" +}; + +template +void DMSECodelet::InsertTask(void *apDescError, void *apDescZPredict, void *apDescZMiss) { + int row, rows_num; + auto pDesc_Z_predict = (CHAM_desc_t *) apDescZPredict; + + for (row = 0; row < pDesc_Z_predict->mt; row++) { + rows_num = row == pDesc_Z_predict->mt - 1 ? pDesc_Z_predict->m - row * pDesc_Z_predict->mb : pDesc_Z_predict->mb; + starpu_insert_task(&this->cl_dmse, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescError, 0, 0), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZPredict, row, 0), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZMiss, row, 0), + 0); + } +} + +template +void DMSECodelet::cl_dmse_function(void **apBuffers, void *apCodeletArguments) { + int rows_num; + T *pZPredict, *pZMiss, *pError; + T local_error = 0.0; + + pError = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pZPredict = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pZMiss = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + + starpu_codelet_unpack_args(apCodeletArguments, &rows_num); + for (int i = 0; i < rows_num; i++) { + local_error += pow((pZPredict[i] - pZMiss[i]), 2); + } + *pError += local_error; +} diff --git a/src/runtime/starpu/concrete/dtrace-codelet.cpp b/src/runtime/starpu/concrete/dtrace-codelet.cpp new file mode 100644 index 00000000..31e6cb5b --- /dev/null +++ b/src/runtime/starpu/concrete/dtrace-codelet.cpp @@ -0,0 +1,78 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dtrace-codelet.cpp + * @brief A class for starpu codelet dtrace. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#include + +#include +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet DTRACECodelet::cl_dtrace = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_dtrace_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_dtrace_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 3, + .modes = {STARPU_R, STARPU_RW, STARPU_W}, + .name = "dtrace" +}; + +template +void DTRACECodelet::InsertTask(void *apDescA, void *apDescNum, void *apDescTrace) { + int row, rows_num; + auto pDescriptor_A = (CHAM_desc_t *) apDescA; + + for (row = 0; row < pDescriptor_A->mt; row++) { + rows_num = row == pDescriptor_A->mt - 1 ? pDescriptor_A->m - row * pDescriptor_A->mb : pDescriptor_A->mb; + starpu_insert_task(&this->cl_dtrace, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescA, row, row), + STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescNum, 0, 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescTrace, row, 0), + 0); + } +} + +template +void DTRACECodelet::cl_dtrace_function(void *apBuffers[], void *apCodeletArguments) { + int rows_num; + T *pDescriptor_A, *pSum , *pTrace; + + pDescriptor_A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pSum = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pTrace = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + + starpu_codelet_unpack_args(apCodeletArguments, &rows_num); + T local_sum = core_dtrace(pDescriptor_A, rows_num, pTrace); + *pSum += local_sum; +} + +template +double DTRACECodelet::core_dtrace(const T *pDescriptor, const int &aSize, T *pTrace) { + T result = 0.0; + for (int i = 0; i < aSize; i++) { + result += pDescriptor[i + i * aSize]; + pTrace[i] = pDescriptor[i + i * aSize]; + } + return result; +} diff --git a/src/runtime/starpu/concrete/dzcpy-codelet.cpp b/src/runtime/starpu/concrete/dzcpy-codelet.cpp new file mode 100644 index 00000000..c279d4a5 --- /dev/null +++ b/src/runtime/starpu/concrete/dzcpy-codelet.cpp @@ -0,0 +1,65 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file dzcpy-codelet.cpp + * @brief A class for starpu codelet dzcpy. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#include + +#include +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet DZCPYCodelet::cl_dzcpy = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_dzcpy_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_dzcpy_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 1, + .modes = {STARPU_W}, + .name = "dzcpy" +}; + +template +void DZCPYCodelet::InsertTask(void *apDescriptor, void *apDoubleVector) { + int row, tile_row, rows_num; + auto pDescriptor_A = (CHAM_desc_t *) apDescriptor; + + for (row = 0; row < pDescriptor_A->mt; row++) { + rows_num = row == pDescriptor_A->mt - 1 ? pDescriptor_A->m - row * pDescriptor_A->mb : pDescriptor_A->mb; + tile_row = row * pDescriptor_A->mb; + starpu_insert_task(&this->cl_dzcpy, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_VALUE, &tile_row, sizeof(int), + STARPU_VALUE, &apDoubleVector, sizeof(double), + STARPU_W, RUNTIME_data_getaddr(pDescriptor_A, row, 0), + 0); + } +} + +template +void DZCPYCodelet::cl_dzcpy_function(void **apBuffers, void *apCodeletArguments) { + int rows_num, tile_row; + T *pDescriptor_A, *pR; + + pDescriptor_A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + starpu_codelet_unpack_args(apCodeletArguments, &rows_num, &tile_row, &pR); + memcpy(pDescriptor_A, &pR[tile_row], rows_num * sizeof(T)); +} diff --git a/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp b/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp new file mode 100644 index 00000000..e0127324 --- /dev/null +++ b/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp @@ -0,0 +1,93 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file gaussian-to-non-codelet.cpp + * @brief A class for starpu codelet gaussian-to-non. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#include + +#include +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet GaussianCodelet::cl_gaussian_to_non = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_gaussian_to_non_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where = STARPU_CPU, + .cpu_funcs = {cl_gaussian_to_non_function}, + .nbuffers = 1, + .modes = {STARPU_RW}, + .name = "gaussian_to_non" +#endif +}; + +template +void GaussianCodelet::InsertTask(void *apDesc, T *apTheta) { + int row, rows_num; + auto pDescriptor_Z = (CHAM_desc_t *) apDesc; + + for (row = 0; row < pDescriptor_Z->mt; row++) { + rows_num = row == pDescriptor_Z->mt - 1 ? pDescriptor_Z->m - row * pDescriptor_Z->mb : pDescriptor_Z->mb; + starpu_insert_task(&this->cl_gaussian_to_non, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr(pDescriptor_Z, row, 0), + STARPU_VALUE, &apTheta[0], sizeof(T), + STARPU_VALUE, &apTheta[1], sizeof(T), + STARPU_VALUE, &apTheta[2], sizeof(T), + STARPU_VALUE, &apTheta[3], sizeof(T), + STARPU_VALUE, &apTheta[4], sizeof(T), + STARPU_VALUE, &apTheta[5], sizeof(T), + 0); + } +} + +template +void GaussianCodelet::cl_gaussian_to_non_function(void **apBuffers, void *apCodeletArguments) { + int rows_num; + T *pDescriptorZ, *pTheta; + + pTheta = new T[6]; + pDescriptorZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + + starpu_codelet_unpack_args(apCodeletArguments, &rows_num, &pTheta[0], &pTheta[1], &pTheta[2], &pTheta[3], &pTheta[4], + &pTheta[5]); + //core function to convert Z tile from Gaussian to non-Gaussian. + core_gaussian_to_non(pDescriptorZ, pTheta, rows_num); + delete[] pTheta; +} + +template +void GaussianCodelet::core_gaussian_to_non(T *apDescriptorZ,const T *apLocalTheta, const int &aSize) { + + T xi = apLocalTheta[2]; + T omega = apLocalTheta[3]; + T g = apLocalTheta[4]; + T h = apLocalTheta[5]; + + int i; + if (h < 0) { + throw std::runtime_error("The kurtosis parameter cannot be negative"); + } + if (g == 0) { + for (i = 0; i < aSize; i++) + apDescriptorZ[i] = xi + omega * apDescriptorZ[i] * (exp(0.5 * h * pow(apDescriptorZ[i], 2))); + } else { + for (i = 0; i < aSize; i++) + apDescriptorZ[i] = xi + omega * (exp(g * apDescriptorZ[i]) - 1) * (exp(0.5 * h * pow(apDescriptorZ[i], 2))) / g; + } +} + diff --git a/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp b/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp new file mode 100644 index 00000000..cb38b2e3 --- /dev/null +++ b/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp @@ -0,0 +1,99 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file non-gaussian-codelet-loglike-codelet.cpp + * @brief A class for starpu codelet non-gaussian-loglike. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-26 +**/ + +#include + +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet NonGaussianLoglike::cl_non_gaussian_loglike = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_non_gaussian_loglike_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_non_gaussian_loglike_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 2, + .modes = {STARPU_R, STARPU_RW}, + .name = "non_gaussian_loglike" +}; + +template +void NonGaussianLoglike::InsertTask(void *apDescZ, void *apDescSum, const T *apTheta, + std::unique_ptr &aStarPuHelpers) { + auto desc_mt = aStarPuHelpers->GetMT(apDescZ); + auto desc_m = aStarPuHelpers->GetM(apDescZ); + auto desc_mb = aStarPuHelpers->GetMB(apDescZ); + + int row, rows_num; + for (row = 0; row < desc_mt; row++) { + rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; + starpu_insert_task(&this->cl_non_gaussian_loglike, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_R, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescZ, row, 0), + STARPU_RW, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescSum, 0, 0), + STARPU_VALUE, &apTheta[0], sizeof(double), + STARPU_VALUE, &apTheta[1], sizeof(double), + STARPU_VALUE, &apTheta[2], sizeof(double), + STARPU_VALUE, &apTheta[3], sizeof(double), + STARPU_VALUE, &apTheta[4], sizeof(double), + STARPU_VALUE, &apTheta[5], sizeof(double), + 0); + } +} + +template +void NonGaussianLoglike::cl_non_gaussian_loglike_function(void **apBuffers, void *apCodeletArguments) { + int rows_num; + T *pDescriptor_Z, *pDescriptor_sum; + + auto *pTheta = new T[6]; + pDescriptor_Z = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pDescriptor_sum = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + + starpu_codelet_unpack_args(apCodeletArguments, &rows_num, &pTheta[0], &pTheta[1], &pTheta[2], &pTheta[3], + &pTheta[4], + &pTheta[5]); + T local_sum = core_non_gaussian_loglike_helper(pDescriptor_Z, pTheta, rows_num); + *pDescriptor_sum += local_sum; + delete[] pTheta; +} + +template +double NonGaussianLoglike::core_non_gaussian_loglike_helper(const T *apDescriptorZ, const T *apLocalTheta, const int &aSize) { + T g = apLocalTheta[4]; + T h = apLocalTheta[5]; + + int i; + T sum = 0.0; + if (h < 0) { + throw std::runtime_error("The kurtosis parameter cannot be negative"); + + } + for (i = 0; i < aSize; i++) { + if (g == 0) + sum += log(1 + h * pow(apDescriptorZ[i], 2)) + 0.5 * h * pow(apDescriptorZ[i], 2); + else { + sum += log(exp(g * apDescriptorZ[i]) + (exp(g * apDescriptorZ[i]) - 1) * h * apDescriptorZ[i] / g) + 0.5 * h * pow(apDescriptorZ[i], 2); + } + } + return sum; +} diff --git a/src/runtime/starpu/concrete/non-gaussian-transform-codelet.cpp b/src/runtime/starpu/concrete/non-gaussian-transform-codelet.cpp new file mode 100644 index 00000000..a6c5900a --- /dev/null +++ b/src/runtime/starpu/concrete/non-gaussian-transform-codelet.cpp @@ -0,0 +1,136 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file non-gaussian-codelet-transform-codelet.cpp + * @brief A class for starpu codelet non-gaussian-transform. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-26 +**/ + +#include +#include + +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet NonGaussianTransform::cl_non_gaussian_transform = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_non_gaussian_transform_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_non_gaussian_transform_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 1, + .modes = {STARPU_RW}, + .name = "non_gaussian_transform" +}; + +template +void +NonGaussianTransform::InsertTask(void *apDescZ, const T *apTheta, std::unique_ptr &apStarPuHelpers) { + int row, rows_num; + + auto desc_mt = apStarPuHelpers->GetMT(apDescZ); + auto desc_m = apStarPuHelpers->GetM(apDescZ); + auto desc_mb = apStarPuHelpers->GetMB(apDescZ); + + for (row = 0; row < desc_mt; row++) { + rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; + starpu_insert_task(&this->cl_non_gaussian_transform, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_RW, apStarPuHelpers->ExaGeoStatDataGetAddr(apDescZ, row, 0), + STARPU_VALUE, &apTheta[0], sizeof(double), + STARPU_VALUE, &apTheta[1], sizeof(double), + STARPU_VALUE, &apTheta[2], sizeof(double), + STARPU_VALUE, &apTheta[3], sizeof(double), + STARPU_VALUE, &apTheta[4], sizeof(double), + STARPU_VALUE, &apTheta[5], sizeof(double), + 0); + } +} + +template +void NonGaussianTransform::cl_non_gaussian_transform_function(void **apBuffers, void *apCodeletArguments) { + int rows_num; + T *pDescriptorZ, *pTheta; + + pTheta = new T[6]; + pDescriptorZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + + starpu_codelet_unpack_args(apCodeletArguments, &rows_num, &pTheta[0], &pTheta[1], &pTheta[2], &pTheta[3], + &pTheta[4], + &pTheta[5]); + core_non_gaussian_transform_helper(pDescriptorZ, pTheta, rows_num); + delete[] pTheta; +} + +template +void +NonGaussianTransform::core_non_gaussian_transform_helper(T *apDescripZ, const T *apLocalTheta, const int &aSize) { + + T xi = apLocalTheta[2]; + T omega = apLocalTheta[3]; + T g = apLocalTheta[4]; + T h = apLocalTheta[5]; + T eps = 1.0e-5; + + for (int i = 0; i < aSize; i++) + apDescripZ[i] = newton_raphson(apDescripZ[i], xi, omega, g, h, eps); +} + +template +double +NonGaussianTransform::newton_raphson(const T apDescriptorZ, const T aTransLocation, const T aTransScale, + const T aTransShape, const T aTransKurtosis, const T aEpsilon) { + int itr, max_itr; + T x0 = 0, x1, all_err, diff; + all_err = aEpsilon; + max_itr = 1000; + for (itr = 1; itr <= max_itr; itr++) { + diff = tukeyGHTransfor(apDescriptorZ, x0, aTransLocation, aTransScale, aTransShape, aTransKurtosis) / + tukeyGHDiferencial(x0, aTransScale, aTransShape, aTransKurtosis); + x1 = x0 - diff; + if (fabs(diff) < all_err) + return x1; + x0 = x1; + } + return x1; +} + +template +double NonGaussianTransform::tukeyGHTransfor(const T aOriginalValue, const T aCurrentValue, const T aTransLocation, + const T aTransScale, const T aTransShape, const T aTransKurtosis) { + if (aTransShape == 0) + return aOriginalValue - aTransLocation - + aTransScale * aCurrentValue * exp(0.5 * aTransKurtosis * aCurrentValue * aCurrentValue); + else + return aOriginalValue - aTransLocation - (aTransScale * (exp(aTransShape * aCurrentValue) - 1) * + (exp(0.5 * aTransKurtosis * aCurrentValue * aCurrentValue)) / + aTransShape); +} + +template +double NonGaussianTransform::tukeyGHDiferencial(const T aCurrentValue, const T aTransScale, const T aTransShape, + const T aTransKurtosis) { + if (aTransShape == 0) + return -aTransScale * exp((aTransKurtosis * aCurrentValue * aCurrentValue) / 2.0) - + aTransScale * aTransKurtosis * aCurrentValue * aCurrentValue * + exp((aTransKurtosis * aCurrentValue * aCurrentValue) / 2.0); + else + return -aTransScale * exp(aTransShape * aCurrentValue) * + exp((aTransKurtosis * aCurrentValue * aCurrentValue) / 2.0) - + (aTransKurtosis * aCurrentValue * exp((aTransKurtosis * aCurrentValue * aCurrentValue) / 2.0) * + (aTransScale * exp(aTransShape * aCurrentValue) - aTransScale)) / aTransShape; +} diff --git a/src/runtime/starpu/concrete/stride-vec-codelet.cpp b/src/runtime/starpu/concrete/stride-vec-codelet.cpp new file mode 100644 index 00000000..52c23a10 --- /dev/null +++ b/src/runtime/starpu/concrete/stride-vec-codelet.cpp @@ -0,0 +1,76 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file stride-vec-codelet.cpp + * @brief A class for starpu codelet stride-vec. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#include + +#include +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet STRIDEVECCodelet::cl_stride_vec = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_stride_vec_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_stride_vec_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 3, + .modes = {STARPU_R, STARPU_W, STARPU_W}, + .name = "stride_vec" +}; + +template +void STRIDEVECCodelet::InsertTask(const void *apDescA, void *apDescB, void *apDescC) { + int row, rows_num; + const auto desc_A = (CHAM_desc_t *) apDescA; + auto desc_B = (CHAM_desc_t *) apDescB; + auto desc_C = (CHAM_desc_t *) apDescC; + + auto desc_mt = desc_A->mt; + auto desc_m = desc_A->m; + auto desc_mb = desc_A->mb; + + for (row = 0; row < desc_mt; row++) { + rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; + starpu_insert_task(&this->cl_stride_vec, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr(desc_A, row, 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr(desc_B, (int) floor(row / 2.0), 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr(desc_C, (int) floor(row / 2.0), 0), + 0); + } +} + +template +void STRIDEVECCodelet::cl_stride_vec_function(void **apBuffers, void *apCodeletArguments) { + int rows_num; + T *pDescriptor_A, *pDescriptor_B, *pDescriptor_C; + + pDescriptor_A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pDescriptor_B = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pDescriptor_C = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + starpu_codelet_unpack_args(apCodeletArguments, &rows_num); + + for (int i = 0, j = 0; i < rows_num - 1; i += 2, j++) { + pDescriptor_B[j] = pDescriptor_A[i]; + pDescriptor_C[j] = pDescriptor_A[i + 1]; + } +} diff --git a/src/runtime/starpu/concrete/tri-stride-vec-codelet.cpp b/src/runtime/starpu/concrete/tri-stride-vec-codelet.cpp new file mode 100644 index 00000000..6006e553 --- /dev/null +++ b/src/runtime/starpu/concrete/tri-stride-vec-codelet.cpp @@ -0,0 +1,83 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file tri-stride-vec-codelet.cpp + * @brief A class for starpu codelet tri-stride-vec. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @author Sameh Abdulah + * @date 2024-02-25 +**/ + +#include + +#include +#include + +using namespace exageostat::runtime; + +template +struct starpu_codelet TriStrideVecCodelet::cl_tri_stride_vec = { +#ifdef USE_CUDA + .where= STARPU_CPU | STARPU_CUDA, + .cpu_funcs={cl_tri_stride_vec_function}, + .cuda_funcs={}, + .cuda_flags={0}, +#else + .where=STARPU_CPU, + .cpu_funcs={cl_tri_stride_vec_function}, + .cuda_funcs={}, + .cuda_flags={(0)}, +#endif + .nbuffers = 4, + .modes = {STARPU_R, STARPU_W, STARPU_W, STARPU_W}, + .name = "tri_stride_vec" +}; + +template +void TriStrideVecCodelet::InsertTask(const void *apDescA, void *apDescB, void *apDescC, void *apDescD) { + int row, rows_num; + const auto desc_A = (CHAM_desc_t *) apDescA; + auto desc_B = (CHAM_desc_t *) apDescB; + auto desc_C = (CHAM_desc_t *) apDescC; + auto desc_D = (CHAM_desc_t *) apDescD; + + auto desc_mt = desc_A->mt; + auto desc_m = desc_A->m; + auto desc_mb = desc_A->mb; + + for (row = 0; row < desc_mt; row++) { + rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; + starpu_insert_task(&this->cl_tri_stride_vec, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr(desc_A, row, 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr(desc_B, (int) floor(row / 3.0), 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr(desc_C, (int) floor(row / 3.0), 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr(desc_D, (int) floor(row / 3.0), 0), + 0); + } +} + +template +void TriStrideVecCodelet::cl_tri_stride_vec_function(void *apBuffers[], void *apCodeletArguments) { + + int rows_num; + T *pDescriptor_A, *pDescriptor_B, *pDescriptor_C, *pDescriptor_D; + + pDescriptor_A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); + pDescriptor_B = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); + pDescriptor_C = (T *) STARPU_MATRIX_GET_PTR(apBuffers[2]); + pDescriptor_D = (T *) STARPU_MATRIX_GET_PTR(apBuffers[3]); + + starpu_codelet_unpack_args(apCodeletArguments, &rows_num); + + //accept only temp divided by three (should be optimized) + for (int j = 0, i = 0; i < rows_num - 1; i += 3, j++) { + pDescriptor_B[j] = pDescriptor_A[i]; + pDescriptor_C[j] = pDescriptor_A[i + 1]; + pDescriptor_D[j] = pDescriptor_A[i + 2]; + } +} diff --git a/src/runtime/starpu/helpers/CMakeLists.txt b/src/runtime/starpu/helpers/CMakeLists.txt new file mode 100644 index 00000000..c18f49a2 --- /dev/null +++ b/src/runtime/starpu/helpers/CMakeLists.txt @@ -0,0 +1,23 @@ + +# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# All rights reserved. +# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +# @file CMakeLists.txt +# @version 1.1.0 +# @brief CMake build script for StarPu functions +# @author Mahmoud ElKarargy +# @date 2024-02-19 + +# Include the concrete implementations of the StarPu helpers classes and StarPu Helpes Factory +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/StarPuHelpersFactory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/concrete/ChameleonStarPuHelpers.cpp + ${SOURCES} +) + +if (USE_HICMA) + list(APPEND SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/concrete/HicmaStarPuHelpers.cpp) +endif () + +set(SOURCES ${SOURCES} PARENT_SCOPE) diff --git a/src/runtime/starpu/helpers/StarPuHelpersFactory.cpp b/src/runtime/starpu/helpers/StarPuHelpersFactory.cpp new file mode 100644 index 00000000..d703e496 --- /dev/null +++ b/src/runtime/starpu/helpers/StarPuHelpersFactory.cpp @@ -0,0 +1,38 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file StarPuHelpersFactory.cpp + * @brief Factory for StarPu helpers. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-25 +**/ + +#include +#include + +#ifdef USE_HICMA + +#include + +#endif + +using namespace std; +using namespace exageostat::common; +using namespace exageostat::runtime; + +unique_ptr StarPuHelpersFactory::CreateStarPuHelper(const Computation &aComputation) { + if (aComputation == EXACT_DENSE || aComputation == DIAGONAL_APPROX) { + return make_unique(); + } else if (aComputation == TILE_LOW_RANK) { +#ifdef USE_HICMA + return make_unique(); +#else + throw runtime_error("Tile low rank generation isn't supported without enabling HiCMA. Use -DUSE_HICMA=ON"); +#endif + } + throw runtime_error("You need to enable whether HiCMA or Chameleon"); +} diff --git a/src/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.cpp b/src/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.cpp new file mode 100644 index 00000000..458bb4e1 --- /dev/null +++ b/src/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.cpp @@ -0,0 +1,61 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file ChameleonStarPuHelpers.cpp + * @brief A class for Chameleon implementation of StarPu helpers. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-25 +**/ + +#include +#include + +using namespace exageostat::runtime; + +void +ChameleonStarPuHelpers::ExaGeoStatOptionsInit(void *apOptions, void *apSequence, void *apRequest) { + + RUNTIME_options_init((RUNTIME_option_t *) apOptions, (CHAM_context_t *) ExaGeoStatHardware::GetChameleonContext(), + (RUNTIME_sequence_t *) apSequence, (RUNTIME_request_t *) apRequest); +} + +void ChameleonStarPuHelpers::ExaGeoStatOptionsFree(void *apOptions) { + RUNTIME_options_ws_free((RUNTIME_option_t *) apOptions); +} + + +void ChameleonStarPuHelpers::ExaGeoStatOptionsFinalize(void *apOptions) { + auto *options = (RUNTIME_option_t *) apOptions; + RUNTIME_options_finalize(options, (CHAM_context_t *) ExaGeoStatHardware::GetChameleonContext()); +} + +void *ChameleonStarPuHelpers::ExaGeoStatDataGetAddr(void *apDescriptor, const int &aDescRow, const int &aDescCol) { + return RUNTIME_data_getaddr((CHAM_desc_t *) apDescriptor, aDescRow, aDescCol); +} + +int ChameleonStarPuHelpers::GetMT(void *apDescriptor) { + auto descriptor = (CHAM_desc_t *) apDescriptor; + return descriptor->mt; +} + +int ChameleonStarPuHelpers::GetM(void *apDescriptor) { + auto descriptor = (CHAM_desc_t *) apDescriptor; + return descriptor->m; +} + +int ChameleonStarPuHelpers::GetMB(void *apDescriptor) { + auto descriptor = (CHAM_desc_t *) apDescriptor; + return descriptor->mb; +} + +void *ChameleonStarPuHelpers::GetOptions() { + return new RUNTIME_option_t; +} + +void ChameleonStarPuHelpers::DeleteOptions(void *apOptions) { + delete (RUNTIME_option_t *) apOptions; +} diff --git a/src/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.cpp b/src/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.cpp new file mode 100644 index 00000000..fa43b9e7 --- /dev/null +++ b/src/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.cpp @@ -0,0 +1,58 @@ + +// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// All rights reserved. +// ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). + +/** + * @file HicmaStarPuHelpers.cpp + * @brief A class for Hicma implementation of StarPu helpers. + * @version 1.1.0 + * @author Mahmoud ElKarargy + * @date 2024-02-25 +**/ + +#include +#include + +using namespace exageostat::runtime; + +void HicmaStarPuHelpers::ExaGeoStatOptionsInit(void *apOptions, void *apSequence, void *apRequest) { + HICMA_RUNTIME_options_init((HICMA_option_t *) apOptions, (HICMA_context_t *) ExaGeoStatHardware::GetHicmaContext(), + (HICMA_sequence_t *) apSequence, (HICMA_request_t *) apRequest); +} + +void HicmaStarPuHelpers::ExaGeoStatOptionsFree(void *apOptions) { + HICMA_RUNTIME_options_ws_free((HICMA_option_t *) apOptions); +} + +void HicmaStarPuHelpers::ExaGeoStatOptionsFinalize(void *apOptions) { + auto *options = (HICMA_option_t *) apOptions; + HICMA_RUNTIME_options_finalize(options, (HICMA_context_t *) ExaGeoStatHardware::GetHicmaContext()); +} + +void *HicmaStarPuHelpers::ExaGeoStatDataGetAddr(void *apDescriptor, const int &aDescRow, const int &aDescCol) { + return HICMA_RUNTIME_data_getaddr((HICMA_desc_t *) apDescriptor, aDescRow, aDescCol); +} + +int HicmaStarPuHelpers::GetMT(void *apDescriptor) { + auto descriptor = (HICMA_desc_t *) apDescriptor; + return descriptor->mt; +} + +int HicmaStarPuHelpers::GetM(void *apDescriptor) { + auto descriptor = (HICMA_desc_t *) apDescriptor; + return descriptor->m; +} + +int HicmaStarPuHelpers::GetMB(void *apDescriptor) { + auto descriptor = (HICMA_desc_t *) apDescriptor; + return descriptor->mb; +} + +void *HicmaStarPuHelpers::GetOptions() { + return new HICMA_option_t; +} + +void HicmaStarPuHelpers::DeleteOptions(void *apOptions) { + delete (HICMA_option_t *) apOptions; +} diff --git a/tests/R-tests/TestDataGeneration.R b/tests/R-tests/TestDataGeneration.R index 5831a657..4a0e50bf 100644 --- a/tests/R-tests/TestDataGeneration.R +++ b/tests/R-tests/TestDataGeneration.R @@ -14,6 +14,7 @@ library("ExaGeoStatCPP") paste("---------------------------------------------------------------------------------------------") paste("ExaGeoStat with Data Generation only - saving data with default path") +# Variables dimension = "3D" ncores <- 4 ngpus <- 0 @@ -21,10 +22,11 @@ problem_size <- 16 dts <- 8 lts <- 0 computation <- "exact" +kernel <- "univariate_matern_stationary" +initial_theta <- c(1,0.1,0.5) -save_data <- TRUE -# if you didn't change the log_path it will be saved in the default path of project_path/synthetic_ds -log_path <- "" +# You need to provide a log_path to save the data. +log_path <- getwd() # data path is where to read data from data_path <- "" # observations file path is where to read observation file @@ -33,10 +35,8 @@ observations_file <- "" recovery_file <- "" hardware <- new(Hardware, computation, ncores, ngpus) -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data, dimension=dimension) -data_source <- new(Data, problem_size, dimension) +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension, log_path=log_path) -exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) # Print the data.. paste("** Locations x") x <- get_locationsX(data=exageostat_data) @@ -54,18 +54,10 @@ print(Z) paste("---------------------------------------------------------------------------------------------") paste("ExaGeoStat with Data Generation - Reading Data") -save_data <- FALSE -log_path <- "" # data path is where to read data from data_path <- "./synthetic_ds/SYN_16_1" -# observations file path is where to read observation file -observations_file <- "" -# recovery file path is where to read recovery file -recovery_file <- "" -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(5,1,1,1,1), paths=c(log_path, data_path, observations_file, recovery_file), save_data=save_data, dimension=dimension) -data_source <- new(Data, problem_size, dimension) -exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension, data_path=data_path) paste("** Locations x - after reading") x <- get_locationsX(data=exageostat_data) diff --git a/tests/R-tests/TestDataModeling.R b/tests/R-tests/TestDataModeling.R index 9e97ed74..b60e935e 100644 --- a/tests/R-tests/TestDataModeling.R +++ b/tests/R-tests/TestDataModeling.R @@ -21,10 +21,13 @@ problem_size <- 16 dts <- 8 lts <- 0 computation <- "diag_approx" +kernel <- "univariate_matern_stationary" +initial_theta <- c(1,0.1,0.5) +lower_bound <- c(0.1,0.1,0.1) +upper_bound <- c(5,5,5) hardware <- new(Hardware, computation, ncores, ngpus) -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5) -exageostat_data <- new(Data, problem_size, dimension) + z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, @@ -46,7 +49,7 @@ locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.43468375677119097 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, 0.942824444953078489) -model_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) +theta <- model_data(matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10, computation=computation, band=1) hardware$finalize_hardware() paste("---------------------------------------------------------------------------------------------") @@ -56,27 +59,16 @@ lts <- 8 computation <- "tlr" hardware <- new(Hardware, computation, ncores, ngpus) -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5) -exageostat_data <- new(Data, problem_size, dimension) -z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, - -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, - 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, - -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, - 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, - 0.290822066007430102) +empty_data <- new(Data, problem_size, dimension) +theta <- model_data(matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, lts=lts, dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10, computation=computation) +hardware$finalize_hardware() -locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, - 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, - 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, - 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, - 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, - 0.877592126344701295) +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with data Modeling only - exact") -locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, - 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, - 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, - 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, - 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, - 0.942824444953078489) +lts <- 0 +computation <- "exact" -model_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) +empty_data <- new(Data, problem_size, dimension) +hardware <- new(Hardware, computation, ncores, ngpus) +theta <- model_data(matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10, computation=computation) diff --git a/tests/R-tests/TestDataPrediction.R b/tests/R-tests/TestDataPrediction.R index 82fe3c85..7722a143 100644 --- a/tests/R-tests/TestDataPrediction.R +++ b/tests/R-tests/TestDataPrediction.R @@ -21,48 +21,52 @@ problem_size <- 16 dts <- 8 lts <- 0 computation <- "exact" +kernel <- "univariate_matern_stationary" +estimated_theta <- c(1,0.1,0.5) hardware <- new(Hardware, computation, ncores, ngpus) -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,1,0,0,0)) -exageostat_data <- new(Data, problem_size, dimension) -z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, - -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, - 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, - -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, - 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, - 0.290822066007430102) - -locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, - 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, - 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, - 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, - 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, - 0.877592126344701295) - -locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, - 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, - 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, - 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, - 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, - 0.942824444953078489) - -predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) + +z_value <- c(-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021) + +locations_x <- c(0.092042420080872822, 0.193041886015106440, 0.330556191348134576, + 0.181612878614480805, 0.370473792629892440, 0.652140077821011688, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.465445944914930965, 0.528267338063630132, 0.974792095826657490, + 0.552452887769893985, 0.877592126344701295) + +locations_y <- c(0.928648813611047563, 0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528) + +test_x <- c(0.347951, 0.62768) +test_y <- c(0.806332, 0.105196) +predict_data(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) paste("---------------------------------------------------------------") paste("ExaGeoStat with data Prediction only - idw") -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,1,0,0)) -predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) +test_measurements = c(-1.05428, -1.47441) +idw_error = idw(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta, test_measurements=test_measurements) +paste("idw error values:") +paste(idw_error) paste("---------------------------------------------------------------") paste("ExaGeoStat with data Prediction only - fisher") -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,0,1,0)) -predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) - +fisher_matrix <- fisher(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) +paste("fisher matrix values:") +paste(fisher_matrix) paste("---------------------------------------------------------------") paste("ExaGeoStat with data Prediction only - MLOE-MMOM") +true_theta <- c(1.1,0.2,0.5) -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(8,lts), eTheta=c(0.9, 0.09, 0.4), iTheta=c(1,0.1,0.5), max_rank = 500, lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), prediction=c(5,0,0,0,1)) -predict_data(hardware=hardware, config=config, data=exageostat_data, matrix=z_value, x=locations_x, y=locations_y) +result_mloe_mmom = mloe_mmom(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta, true_theta=true_theta) +cat(sprintf("MLOE = %.6f", result_mloe_mmom[1]), "\n") +cat(sprintf("MMOM = %.6f", result_mloe_mmom[2]), "\n") diff --git a/tests/R-tests/TestExaGeoStatAPI.R b/tests/R-tests/TestExaGeoStatAPI.R index 440ccc83..a02dda02 100644 --- a/tests/R-tests/TestExaGeoStatAPI.R +++ b/tests/R-tests/TestExaGeoStatAPI.R @@ -21,25 +21,27 @@ dts <- 8 lts <- 0 computation <- "exact" dimension = "2D" +kernel <- "univariate_matern_stationary" +initial_theta <- c(1,0.1,0.5) +lower_bound <- c(0.1,0.1,0.1) +upper_bound <- c(5,5,5) hardware <- new(Hardware, computation, ncores, ngpus) -config <- configurations_init(n=problem_size, cores_gpus=c(ncores, ngpus), kernel="univariate_matern_stationary", computation=computation, tile_size=c(dts,lts), iTheta=c(1,0.1,0.5), lb_ub=list(c(0.1,0.1,0.1),c(5,5,5)), mle_itr=5, prediction=c(6,1,1,1,1), dimension=dimension) -data_source <- new(Data, problem_size, dimension) -exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) -model_data(hardware=hardware, config=config, data=exageostat_data) -predict_data(hardware=hardware, config=config, data=exageostat_data) +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) +estimated_theta <- model_data(data=exageostat_data, kernel=kernel, dts=dts, dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10) + +test_x <- c(0.2, 0.330) +test_y <- c(0.104, 0.14) +predict_data(train_data=list(get_locationsX(data=exageostat_data), get_locationsY(data=exageostat_data), get_Z_measurement_vector(data=exageostat_data, type="chameleon")), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) paste("---------------------------------------------------------------------------------------------") paste("ExaGeoStat with Data Generation only") - -data_source <- new(Data, problem_size, dimension) -new_exageostat_data <- simulate_data(hardware=hardware, config=config, data=data_source) +new_exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) paste("---------------------------------------------------------------------------------------------") paste("ExaGeoStat with data Modeling only") -exageostat_data_modeling <- new(Data, problem_size, dimension) z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, @@ -61,4 +63,13 @@ locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.43468375677119097 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, 0.942824444953078489) -model_data(hardware=hardware, config=config, data=exageostat_data_modeling, matrix=z_value, x=locations_x, y=locations_y) \ No newline at end of file +empty_data <- new(Data, problem_size, "2D") +estimated_theta <- model_data(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10) + +paste("---------------------------------------------------------------------------------------------") +paste("ExaGeoStat with data Prediction only") + +test_x <- c(0.2, 0.330) +test_y <- c(0.104, 0.14) + +predict_data(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) diff --git a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp index 6a0a5182..15cd582d 100644 --- a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp +++ b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp @@ -24,21 +24,146 @@ using namespace std; using namespace exageostat::adapters; void TEST_ALL_R_METHODS() { + const string computation = "exact"; + const int cores_number = 1; + const int gpus_number = 0; + const vector initial_theta = {1, 0.1, 0.5}; + const vector estimated_theta = {0.9, 0.2, 0.5}; + const vector lower_bound = {0.1, 0.1, 0.1}; + const vector upper_bound = {5, 5, 5}; + + auto hardware = ExaGeoStatHardware(computation, cores_number, gpus_number); + SECTION("R METHODS") { +// auto exageostat_data = R_ExaGeoStatLoadData("univariate_matern_stationary", initial_theta, "eg", 16, 0, 8, 0, +// "2D", "", "", "", ""); +// vector estimated_theta = R_ExaGeoStatModelData("exact", "univariate_matern_stationary", "eg", +// lower_bound, upper_bound, 4, 10, 8, 0, "2D", 0, 500,exageostat_data); + + + vector> train_data = { + {0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, + 0.877592126344701295}, + {0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, + 0.942824444953078489}, + {/*-1.272336140360187606, -2.590699695867695773,*/ 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, + 0.290822066007430102} + }; + vector> test_data = { + {0.193041886015106440, 0.330556191348134576}, + {0.103883421072709245, 0.135790035858701447} + }; + auto result = R_ExaGeoStatPredictData("univariate_matern_stationary", "eg", estimated_theta, 8, 0, "2D", train_data, test_data); + for(double i : result){ + cout << i << " "; + } +// delete exageostat_data; + } + SECTION("PREDICTION - Fisher"){ + + vector> train_data = { + {0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, + 0.877592126344701295}, + {0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, + 0.942824444953078489}, + {-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, + 0.290822066007430102} + }; + vector> test_data = { + {0.193041886015106440, 0.330556191348134576}, + {0.103883421072709245, 0.135790035858701447} + }; + auto result = R_ExaGeoStatFisher("univariate_matern_stationary", "eg", estimated_theta, 8, 0, "2D", train_data, test_data); + for(double i : result){ + cout << i << " "; + } + } + + SECTION("PREDICTION - MLOE-MMOM"){ + + vector> train_data = { + {0.092042420080872822, 0.193041886015106440, 0.330556191348134576, + 0.181612878614480805, 0.370473792629892440, 0.652140077821011688, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.465445944914930965, 0.528267338063630132, 0.974792095826657490, + 0.552452887769893985, 0.877592126344701295}, + {0.928648813611047563, 0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528}, + {-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021} + }; + vector> test_data = { + {0.347951, 0.62768}, + {0.806332, 0.105196}, + }; + auto result = R_ExaGeoStatMLOE_MMOM("univariate_matern_stationary", "eg", estimated_theta, initial_theta,8, 0, "2D", train_data, test_data); + for(double i : result){ + cout << i << " "; + } + } + + SECTION("PREDICTION - IDW"){ - auto configurations = R_InitializeArguments(16, "univariate_matern_stationary", {8, 0}, {1, 1}, 1, "exact", - "double", {1, 0}, 0, 1, {1, 0.1, 0.5}, {{0.1, 0.1, 0.1}, {5, 5, 5}}, - {-1, -1, -1}, "standard", "2D", 10, 4, {5, 1, 1, 1, 1}, - {"", "", "", ""}, false); - auto hardware = ExaGeoStatHardware("exact", 1, 0); - auto data_source = new ExaGeoStatData(16, "2D"); - auto exageostat_data = R_ExaGeoStatLoadData(&hardware, configurations, data_source); - R_ExaGeoStatModelData(&hardware, configurations, exageostat_data); - R_ExaGeoStatPredictData(&hardware, configurations, exageostat_data); - - delete exageostat_data; - delete configurations; + vector> train_data = { + {0.092042420080872822, 0.193041886015106440, 0.330556191348134576, + 0.181612878614480805, 0.370473792629892440, 0.652140077821011688, + 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, + 0.465445944914930965, 0.528267338063630132, 0.974792095826657490, + 0.552452887769893985, 0.877592126344701295}, + {0.928648813611047563, 0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, + 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, + 0.627679865720607300, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528}, + {-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, + 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, + -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021} + }; + vector> test_data = { + {0.347951, 0.62768}, + {0.806332, 0.105196}, + }; + vector test_measurements = { + -1.05428, -1.47441 + }; + auto result = R_ExaGeoStatIDW("univariate_matern_stationary", "eg", estimated_theta, 8, 0, "2D", train_data, test_data, test_measurements); + for(double i : result){ + cout << i << " "; + } } + } TEST_CASE("Test R/Rcpp adapters in C++") { diff --git a/tests/cpp-tests/api/TestExaGeoStatApi.cpp b/tests/cpp-tests/api/TestExaGeoStatApi.cpp index a2d84b9c..ff90f295 100644 --- a/tests/cpp-tests/api/TestExaGeoStatApi.cpp +++ b/tests/cpp-tests/api/TestExaGeoStatApi.cpp @@ -19,6 +19,7 @@ using namespace std; using namespace exageostat::common; using namespace exageostat::dataunits; +using namespace exageostat::configurations; void TEST_GENERATE_DATA() { SECTION("Data generation - Observations") @@ -47,8 +48,7 @@ void TEST_GENERATE_DATA() { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(EXACT_DENSE, 4, 0); // Or you could use configurations.GetComputation(). std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); // Define the expected output for desk Z double expected_output_data[] = {-1.272336, -2.590700, 0.512143, -0.163880, 0.313504, -1.474411, 0.161705, @@ -79,7 +79,7 @@ void TEST_MODEL_DATA(Computation aComputation) { configurations.SetDenseTileSize(dts); configurations.SetComputation(aComputation); configurations.SetMaxMleIterations(3); - configurations.SetTolerance(pow(10, -4)); + configurations.SetTolerance(4); vector lb{0.1, 0.1, 0.1}; configurations.SetLowerBounds(lb); @@ -135,8 +135,7 @@ void TEST_MODEL_DATA(Computation aComputation) { data->GetLocations()->SetLocationX(*location_x, N); data->GetLocations()->SetLocationY(*location_y, N); - double log_likelihood = exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, - data, z_matrix); + double log_likelihood = exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(configurations, data, z_matrix); REQUIRE((log_likelihood - expected) == Catch::Approx(0.0).margin(1e-6)); delete[] location_x; @@ -147,10 +146,8 @@ void TEST_MODEL_DATA(Computation aComputation) { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(aComputation, 4, 0); // Or you could use configurations.GetComputation(). std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, - data); - double log_likelihood = exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, - data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(configurations, data); + double log_likelihood = exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(configurations, data); REQUIRE((log_likelihood - expected) == Catch::Approx(0.0).margin(1e-6)); } } @@ -166,7 +163,7 @@ void TEST_PREDICTION() { configurations.SetDenseTileSize(dts); configurations.SetComputation(EXACT_DENSE); configurations.SetMaxMleIterations(3); - configurations.SetTolerance(pow(10, -4)); + configurations.SetTolerance(4); vector lb{0.1, 0.1, 0.1}; configurations.SetLowerBounds(lb); @@ -210,25 +207,25 @@ void TEST_PREDICTION() { vector new_estimated_theta{0.9, 0.09, 0.4}; configurations.SetEstimatedTheta(new_estimated_theta); configurations.SetIsMSPE(true); - exageostat::api::ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); }SECTION("Test Prediction - IDW ONLY") { configurations.SetIsMSPE(false); configurations.SetIsIDW(true); - exageostat::api::ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); }SECTION("Test Prediction - MLOE_MMOM ONLY") { configurations.SetIsMSPE(false); configurations.SetIsIDW(false); vector new_estimated_theta{0.9, 0.09, 0.4}; configurations.SetEstimatedTheta(new_estimated_theta); configurations.SetIsMLOEMMOM(true); - exageostat::api::ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); }SECTION("Test Prediction - FISHER\n") { configurations.SetIsMLOEMMOM(false); configurations.SetIsFisher(true); vector new_estimated_theta{0.9, 0.09, 0.4}; configurations.SetEstimatedTheta(new_estimated_theta); - exageostat::api::ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); }SECTION("Test Prediction - ALL OPERATIONS") { vector new_estimated_theta{0.9, 0.09, 0.4}; configurations.SetEstimatedTheta(new_estimated_theta); @@ -238,16 +235,15 @@ void TEST_PREDICTION() { configurations.SetIsFisher(true); // Setting Estimated with initial theta will require mloe_mmom to be zero configurations.SetEstimatedTheta(initial_theta); - exageostat::api::ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); }SECTION("Test Prediction - ALL MODULES") { configurations.SetIsMSPE(true); configurations.SetIsIDW(true); configurations.SetIsFisher(true); configurations.SetIsMLOEMMOM(true); - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, - data); - exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data); - exageostat::api::ExaGeoStat::ExaGeoStatPrediction(hardware, configurations, data, z_matrix); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(configurations,data); + exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(configurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); } delete[] location_x; delete[] location_y; diff --git a/tests/cpp-tests/configurations/TestConfigurations.cpp b/tests/cpp-tests/configurations/TestConfigurations.cpp index f3b06e9a..d8b2e9e8 100644 --- a/tests/cpp-tests/configurations/TestConfigurations.cpp +++ b/tests/cpp-tests/configurations/TestConfigurations.cpp @@ -23,6 +23,7 @@ using namespace std; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_ARGUMENT_INITIALIZATION(){ @@ -80,7 +81,7 @@ void TEST_ARGUMENT_INITIALIZATION(){ configurations.InitializeDataModelingArguments(); REQUIRE(configurations.GetMaxMleIterations() == 5); - REQUIRE(configurations.GetTolerance() == 4); + REQUIRE(configurations.GetTolerance() == pow(10, -4)); // Data prediction arguments initialized configurations.InitializeDataPredictionArguments(); diff --git a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp index 1ada7d9c..b67b2218 100644 --- a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp @@ -19,6 +19,7 @@ #include #include #include +#include using namespace std; @@ -26,6 +27,8 @@ using namespace exageostat::generators; using namespace exageostat::dataunits; using namespace exageostat::common; using namespace exageostat::kernels; +using namespace exageostat::configurations; +using namespace exageostat::dataLoader::csv; void TEST_CSV_P_1() { int N = 16; @@ -85,8 +88,8 @@ void TEST_CSV_P_1() { locations.SetLocationX(*location_x, N); locations.SetLocationY(*location_y, N); - exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N, p, write_path, locations); - auto data = csv_reader->CreateData(configurations, hardware, *pKernel); + CSVLoader::GetInstance()->WriteData(*measurements_matrix, N, p, write_path, locations); + auto data = csv_reader->CreateData(configurations, *pKernel); auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, data->GetDescriptorData()->GetDescriptor( @@ -118,8 +121,8 @@ void TEST_CSV_P_1() { locations.SetLocationY(*location_y, N); locations.SetLocationZ(*location_z, N); - exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N, p, write_path, locations); - auto data = csv_reader->CreateData(configurations, hardware, *pKernel); + CSVLoader::GetInstance()->WriteData(*measurements_matrix, N, p, write_path, locations); + auto data = csv_reader->CreateData(configurations, *pKernel); auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, data->GetDescriptorData()->GetDescriptor( @@ -154,8 +157,8 @@ void TEST_CSV_P_1() { locations.SetLocationY(*location_y, N); locations.SetLocationZ(*location_time, N); - exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N, p, write_path, locations); - auto data = csv_reader->CreateData(configurations, hardware, *pKernel); + CSVLoader::GetInstance()->WriteData(*measurements_matrix, N, p, write_path, locations); + auto data = csv_reader->CreateData(configurations, *pKernel); auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, data->GetDescriptorData()->GetDescriptor( @@ -247,9 +250,9 @@ void TEST_CSV_P_2() { locations.SetLocationX(*location_x, N); locations.SetLocationY(*location_y, N); - exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, + CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, locations); - auto data = csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, *pKernel); auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, data->GetDescriptorData()->GetDescriptor( @@ -284,9 +287,9 @@ void TEST_CSV_P_2() { locations.SetLocationY(*location_y, N); locations.SetLocationZ(*location_z, N); - exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, + CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, locations); - auto data = csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, *pKernel); auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, data->GetDescriptorData()->GetDescriptor( @@ -324,9 +327,9 @@ void TEST_CSV_P_2() { locations.SetLocationY(*location_y, N); locations.SetLocationZ(*location_time, N); - exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, + CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, locations); - auto data = csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, *pKernel); auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, data->GetDescriptorData()->GetDescriptor( @@ -417,9 +420,9 @@ void TEST_CSV_P_3() { locations.SetLocationX(*location_x, N); locations.SetLocationY(*location_y, N); - exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, + CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, locations); - auto data = csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, *pKernel); auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, data->GetDescriptorData()->GetDescriptor( @@ -452,9 +455,9 @@ void TEST_CSV_P_3() { locations.SetLocationY(*location_y, N); locations.SetLocationZ(*location_z, N); - exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, + CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, locations); - auto data = csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, *pKernel); auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, data->GetDescriptorData()->GetDescriptor( @@ -490,9 +493,9 @@ void TEST_CSV_P_3() { locations.SetLocationY(*location_y, N); locations.SetLocationZ(*location_time, N); - exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N * p, p, write_path, + CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, locations); - auto data = csv_reader->CreateData(configurations, hardware, *pKernel); + auto data = csv_reader->CreateData(configurations, *pKernel); auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, data->GetDescriptorData()->GetDescriptor( diff --git a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp index e6d02d74..75d5e088 100644 --- a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp @@ -31,6 +31,7 @@ using namespace exageostat::dataunits; using namespace exageostat::common; using namespace exageostat::kernels; using namespace exageostat::helpers; +using namespace exageostat::configurations; void TEST_SPREAD_REVERSED_BITS() { @@ -205,7 +206,7 @@ void TEST_GENERATE_LOCATIONS() { synthetic_data_configurations); synthetic_data_configurations.SetDimension(Dimension2D); - auto data = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); + auto data = synthetic_generator->CreateData(synthetic_data_configurations, *pKernel); double *x = data->GetLocations()->GetLocationX(); double *y = data->GetLocations()->GetLocationY(); @@ -221,8 +222,7 @@ void TEST_GENERATE_LOCATIONS() { synthetic_data_configurations.SetDimension(Dimension3D); unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( synthetic_data_configurations); - auto data = synthetic_generator->CreateData(synthetic_data_configurations, - hardware, *pKernel); + auto data = synthetic_generator->CreateData(synthetic_data_configurations, *pKernel); double *x = data->GetLocations()->GetLocationX(); double *y = data->GetLocations()->GetLocationY(); @@ -241,8 +241,7 @@ void TEST_GENERATE_LOCATIONS() { synthetic_data_configurations.SetTimeSlot(2); unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( synthetic_data_configurations); - auto data = synthetic_generator->CreateData(synthetic_data_configurations, - hardware, *pKernel); + auto data = synthetic_generator->CreateData(synthetic_data_configurations, *pKernel); double *x = data->GetLocations()->GetLocationX(); double *y = data->GetLocations()->GetLocationY(); @@ -310,7 +309,7 @@ void TEST_GENERATION() { // Initialize the seed manually with zero, to get the first generated seeded numbers. int seed = 0; srand(seed); - auto data = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); + auto data = synthetic_generator->CreateData(synthetic_data_configurations, *pKernel); // The expected output of the locations. vector x = {0.257389, 0.456062, 0.797269, 0.242161, 0.440742, 0.276432, 0.493965, 0.953933, 0.86952}; vector y = {0.138506, 0.238193, 0.170245, 0.579583, 0.514397, 0.752682, 0.867704, 0.610986, 0.891279}; @@ -321,7 +320,7 @@ void TEST_GENERATION() { } // Now test re-generating locations again, but without modifying seed manually which will results in completely new locations values - auto data1 = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); + auto data1 = synthetic_generator->CreateData(synthetic_data_configurations, *pKernel); for (int i = 0; i < N; i++) { REQUIRE((data1->GetLocations()->GetLocationX()[i] - x[i]) != Catch::Approx(0.0).margin(1e-6)); REQUIRE((data1->GetLocations()->GetLocationY()[i] - y[i]) != Catch::Approx(0.0).margin(1e-6)); @@ -330,7 +329,7 @@ void TEST_GENERATION() { // Now if we modified seed again, we will get the first generated locations again. int seed_srand = 0; srand(seed_srand); - auto data2 = synthetic_generator->CreateData(synthetic_data_configurations, hardware, *pKernel); + auto data2 = synthetic_generator->CreateData(synthetic_data_configurations, *pKernel); for (int i = 0; i < N; i++) { REQUIRE((data2->GetLocations()->GetLocationX()[i] - x[i]) == Catch::Approx(0.0).margin(1e-6)); REQUIRE((data2->GetLocations()->GetLocationY()[i] - y[i]) == Catch::Approx(0.0).margin(1e-6)); diff --git a/tests/cpp-tests/data-units/TestDescriptorData.cpp b/tests/cpp-tests/data-units/TestDescriptorData.cpp index ef332149..0615fc21 100644 --- a/tests/cpp-tests/data-units/TestDescriptorData.cpp +++ b/tests/cpp-tests/data-units/TestDescriptorData.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; using namespace exageostat::dataunits; +using namespace exageostat::configurations; void TEST_CHAM_TO_HICMA_CONV(){ @@ -33,7 +34,6 @@ void TEST_CHAM_TO_HICMA_CONV(){ int p = 1; auto hardware = ExaGeoStatHardware(EXACT_DENSE, 1, 0); auto linearAlgebraSolver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); - linearAlgebraSolver->SetContext(hardware.GetChameleonContext()); auto *data = new DescriptorData(); linearAlgebraSolver->InitiateDescriptors(synthetic_data_configurations, *data, p); diff --git a/tests/cpp-tests/helpers/TestDiskWriter.cpp b/tests/cpp-tests/helpers/TestDiskWriter.cpp index 0916ad65..705aec50 100644 --- a/tests/cpp-tests/helpers/TestDiskWriter.cpp +++ b/tests/cpp-tests/helpers/TestDiskWriter.cpp @@ -16,10 +16,11 @@ #include #include -#include +#include using namespace exageostat::dataunits; using namespace exageostat::common; +using namespace exageostat::dataLoader::csv; void TEST_3D_VECTORS_WRITING() { // Initialize data vectors @@ -51,7 +52,7 @@ void TEST_3D_VECTORS_WRITING() { std::string expectedFilePath = write_path + "/synthetic_ds/SYN_" + std::to_string(N / p) + "_1"; // Write the data into file - exageostat::helpers::DiskWriter::WriteVectorsToDisk(*measurements_matrix, N, p, write_path, locations); + CSVLoader::GetInstance()->WriteData(*measurements_matrix, N, p, write_path, locations); REQUIRE(std::filesystem::exists(expectedFilePath)); std::ifstream file(expectedFilePath); diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp index 74bf95a8..19aca623 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_BivariateMaternFlexible() { @@ -48,8 +49,7 @@ void TEST_KERNEL_GENERATION_BivariateMaternFlexible() { int seed = 0; srand(seed); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp index 2b8b5101..4bfabee3 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp @@ -22,6 +22,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_BivariateMaternParsimonious() { @@ -49,7 +50,7 @@ void TEST_KERNEL_GENERATION_BivariateMaternParsimonious() { int seed = 0; srand(seed); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations,data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations,data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp index c673306e..09e2ecab 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp @@ -22,6 +22,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_BivariateSpacetimeMaternStationary() { @@ -50,8 +51,7 @@ void TEST_KERNEL_GENERATION_BivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; diff --git a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp index e6d0057e..845e8c95 100644 --- a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp @@ -22,6 +22,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_TrivariateMaternParsimonious() { @@ -47,8 +48,7 @@ void TEST_KERNEL_GENERATION_TrivariateMaternParsimonious() { int seed = 0; srand(seed); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp index 1ed3d570..4a5ce9ac 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp @@ -20,6 +20,7 @@ using namespace std; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternDbeta() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp index d4cd5ecc..c6e0aed0 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternDdbetaBeta() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp index d20272c2..5be19562 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternDdbetaNu() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp index 12ecc421..98659420 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternDdnuNu() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp index a84bbdce..987c3d24 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::common; using namespace exageostat::api; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternDdsigmaSquare() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp index 053a222b..96541419 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternDdsigmaSquareBeta() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp index 22051826..1fe1438e 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternDdsigmaSquareNu() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp index 7577b33d..c9d324fa 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternDnu() { SECTION("UnivariateMaternDnu") diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp index c38c9ac7..7aaf6115 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternDsigmaSquare() { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp index 27e9ce93..5cf79781 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { @@ -51,8 +52,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { int seed = 0; srand(seed); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp index 0142e74c..31376d7d 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp @@ -22,6 +22,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternNuggetsStationary() { @@ -47,8 +48,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNuggetsStationary() { int seed = 0; srand(seed); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp index 7f00ccd5..d2789b0b 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp @@ -22,6 +22,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateMaternStationary() { @@ -48,8 +49,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternStationary() { int seed = 0; srand(seed); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp index 8d85d283..b618a308 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp @@ -8,7 +8,7 @@ * @brief Unit tests for the TestUnivariatePowExpStationary kernel in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestUnivariatePowExpStationary kernel * in the ExaGeoStat software package. The tests cover the generation of data using this kernel with various configurations. - * @version 1.0.0 + * @version 1.1.0 * @author Sameh Abdulah * @author Mahmoud ElKarargy * @date 2024-01-16 @@ -22,6 +22,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariatePowExpStationary() { @@ -48,7 +49,7 @@ void TEST_KERNEL_GENERATION_UnivariatePowExpStationary() { int seed = 0; srand(seed); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp index 526022ba..acb8733c 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp @@ -21,6 +21,7 @@ using namespace std; using namespace exageostat::api; using namespace exageostat::common; +using namespace exageostat::configurations; void TEST_KERNEL_GENERATION_UnivariateSpacetimeMaternStationary() { @@ -49,8 +50,7 @@ void TEST_KERNEL_GENERATION_UnivariateSpacetimeMaternStationary() { int seed = 0; srand(seed); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp index 0b97a447..564d6cab 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp @@ -26,6 +26,7 @@ using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; using namespace exageostat::dataunits; +using namespace exageostat::configurations; //Test that the function initializes the CHAM_descriptorC descriptor correctly. @@ -39,8 +40,6 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES_DST() { auto hardware = ExaGeoStatHardware(DIAGONAL_APPROX, 1, 0); auto linearAlgebraSolver = LinearAlgebraFactory::CreateLinearAlgebraSolver(DIAGONAL_APPROX); - linearAlgebraSolver->SetContext(hardware.GetChameleonContext()); - synthetic_data_configurations.SetProblemSize(64); synthetic_data_configurations.SetDenseTileSize(16); @@ -163,8 +162,6 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES_DST() { auto hardware = ExaGeoStatHardware(DIAGONAL_APPROX, 2, 0); auto linearAlgebraSolver = LinearAlgebraFactory::CreateLinearAlgebraSolver(DIAGONAL_APPROX); - linearAlgebraSolver->SetContext(hardware.GetChameleonContext()); - synthetic_data_configurations.SetProblemSize(32); synthetic_data_configurations.SetDenseTileSize(16); diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp index 61839081..83e5bfb7 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp @@ -26,6 +26,7 @@ using namespace std; using namespace exageostat::linearAlgebra; using namespace exageostat::common; using namespace exageostat::dataunits; +using namespace exageostat::configurations; //Test that the function initializes the CHAM_descriptorC descriptor correctly. void TEST_CHAMELEON_DESCRIPTORS_VALUES() { @@ -39,8 +40,6 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES() { auto hardware = ExaGeoStatHardware(EXACT_DENSE, 1, 0); auto linearAlgebraSolver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); - linearAlgebraSolver->SetContext(hardware.GetChameleonContext()); - synthetic_data_configurations.SetProblemSize(4); synthetic_data_configurations.SetDenseTileSize(1); @@ -163,8 +162,6 @@ void TEST_CHAMELEON_DESCRIPTORS_VALUES() { auto hardware = ExaGeoStatHardware(EXACT_DENSE, 4, 0); auto linearAlgebraSolver = LinearAlgebraFactory::CreateLinearAlgebraSolver(EXACT_DENSE); - linearAlgebraSolver->SetContext(hardware.GetChameleonContext()); - synthetic_data_configurations.SetProblemSize(64); synthetic_data_configurations.SetDenseTileSize(8); diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp index 62a5ce94..3bf617cc 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp @@ -19,6 +19,7 @@ using namespace std; using namespace exageostat::common; using namespace exageostat::dataunits; +using namespace exageostat::configurations; //Test that the function initializes the HICMA_descriptorC descriptor correctly. void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { @@ -26,7 +27,7 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { Configurations synthetic_data_configurations; synthetic_data_configurations.SetComputation(exageostat::common::TILE_LOW_RANK); synthetic_data_configurations.SetMaxMleIterations(1); - synthetic_data_configurations.SetTolerance(pow(10, -4)); + synthetic_data_configurations.SetTolerance(4); vector lb{0.1, 0.1, 0.1}; synthetic_data_configurations.SetLowerBounds(lb); @@ -51,9 +52,8 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { synthetic_data_configurations.SetApproximationMode(1); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); - exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, synthetic_data_configurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(synthetic_data_configurations, data); auto *HICMA_descriptorC = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_C).hicma_desc; int approximationMode = synthetic_data_configurations.GetApproximationMode(); @@ -85,9 +85,8 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { auto hardware = ExaGeoStatHardware(TILE_LOW_RANK, 1, 0); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(hardware, synthetic_data_configurations, - data); - exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(hardware, synthetic_data_configurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); + exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(synthetic_data_configurations, data); int N = synthetic_data_configurations.GetProblemSize(); int lts = synthetic_data_configurations.GetLowTileSize(); diff --git a/tests/cpp-tests/prediction/TestPrediction.cpp b/tests/cpp-tests/prediction/TestPrediction.cpp index 74f1318f..31bce777 100644 --- a/tests/cpp-tests/prediction/TestPrediction.cpp +++ b/tests/cpp-tests/prediction/TestPrediction.cpp @@ -7,7 +7,7 @@ * @file TestPrediction.cpp * @brief Unit tests for the TestPrediction class in the ExaGeoStat software package. * @details This file contains Catch2 unit tests that validate the functionality of the TestPrediction class - * @version 1.0.0 + * @version 1.1.0 * @author Mahmoud ElKarargy * @date 2024-1-18 **/ @@ -20,6 +20,8 @@ using namespace std; using namespace exageostat::common; using namespace exageostat::prediction; +using namespace exageostat::results; +using namespace exageostat::configurations; void TEST_PREDICTION_MISSING_DATA() { @@ -34,7 +36,7 @@ void TEST_PREDICTION_MISSING_DATA() { configurations.SetDenseTileSize(dts); configurations.SetComputation(EXACT_DENSE); configurations.SetMaxMleIterations(3); - configurations.SetTolerance(pow(10, -4)); + configurations.SetTolerance(4); vector lb{0.1, 0.1, 0.1}; configurations.SetLowerBounds(lb); @@ -89,9 +91,9 @@ void TEST_PREDICTION_MISSING_DATA() { // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); - Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + Prediction::PredictMissingData(data, configurations, z_matrix, *pKernel); - REQUIRE(exageostat::results::Results::GetInstance()->GetMSPEError()== Catch::Approx(0.552448)); + REQUIRE(Results::GetInstance()->GetMSPEError()== Catch::Approx(0.552448)); delete pKernel; } SECTION("Test Prediction - IDW ") { @@ -110,9 +112,9 @@ void TEST_PREDICTION_MISSING_DATA() { configurations.GetTimeSlot()); // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); - Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + Prediction::PredictMissingData(data, configurations, z_matrix, *pKernel); for(int i =0;i<3;i++){ - REQUIRE(exageostat::results::Results::GetInstance()->GetIDWError()[i] == Catch::Approx(idw_error[i])); + REQUIRE(Results::GetInstance()->GetIDWError()[i] == Catch::Approx(idw_error[i])); } delete pKernel; } @@ -131,18 +133,18 @@ void TEST_PREDICTION_MISSING_DATA() { configurations.GetTimeSlot()); // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); - Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); - REQUIRE(exageostat::results::Results::GetInstance()->GetMLOE() == Catch::Approx(0.004467).margin(0.001)); - REQUIRE(exageostat::results::Results::GetInstance()->GetMMOM() == Catch::Approx(-0.0812376).margin(0.001)); + Prediction::PredictMissingData(data, configurations, z_matrix, *pKernel); + REQUIRE(Results::GetInstance()->GetMLOE() == Catch::Approx(0.004467).margin(0.001)); + REQUIRE(Results::GetInstance()->GetMMOM() == Catch::Approx(-0.0812376).margin(0.001)); vector new_estimated_theta1{1, 0.1, 0.5}; configurations.SetEstimatedTheta(new_estimated_theta1); // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); - Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); - REQUIRE(exageostat::results::Results::GetInstance()->GetMLOE() == Catch::Approx(0).margin(0.001)); - REQUIRE(exageostat::results::Results::GetInstance()->GetMMOM() == Catch::Approx(0).margin(0.001)); + Prediction::PredictMissingData(data, configurations, z_matrix, *pKernel); + REQUIRE(Results::GetInstance()->GetMLOE() == Catch::Approx(0).margin(0.001)); + REQUIRE(Results::GetInstance()->GetMMOM() == Catch::Approx(0).margin(0.001)); delete pKernel; } @@ -160,11 +162,13 @@ void TEST_PREDICTION_MISSING_DATA() { configurations.GetTimeSlot()); // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); - Prediction::PredictMissingData(hardware, data, configurations, z_matrix, *pKernel); + Prediction::PredictMissingData(data, configurations, z_matrix, *pKernel); - REQUIRE(exageostat::results::Results::GetInstance()->GetFisher00() == Catch::Approx(0.104589)); - REQUIRE(exageostat::results::Results::GetInstance()->GetFisher11() == Catch::Approx(0.187355)); - REQUIRE(exageostat::results::Results::GetInstance()->GetFisher22() == Catch::Approx(10.556483)); + vector required_fisher = {0.1045891821, 0.0005116817, 0.0409307011, 0.0005116817, 0.1873553354, -1.3659618079, 0.0409307011, -1.3659618079, 10.5564826575}; + for(int i = 0; i < Results::GetInstance()->GetFisherMatrix().size(); i++){ + double diff = required_fisher[i] - Results::GetInstance()->GetFisherMatrix()[i]; + REQUIRE(diff == Catch::Approx(0.0).margin(1e-6)); + } delete pKernel; } SECTION("Test Prediction - Exception"){ @@ -176,7 +180,7 @@ void TEST_PREDICTION_MISSING_DATA() { exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( configurations.GetKernelName(), configurations.GetTimeSlot()); - REQUIRE_THROWS(predictor.PredictMissingData(hardware, data, configurations, z_matrix, *pKernel)); + REQUIRE_THROWS(predictor.PredictMissingData(data, configurations, z_matrix, *pKernel)); delete pKernel; } delete[] location_x; diff --git a/tests/cpp-tests/prediction/TestPredictionHelpers.cpp b/tests/cpp-tests/prediction/TestPredictionHelpers.cpp index 8eeb92dc..99acc5e8 100644 --- a/tests/cpp-tests/prediction/TestPredictionHelpers.cpp +++ b/tests/cpp-tests/prediction/TestPredictionHelpers.cpp @@ -243,7 +243,7 @@ void TEST_SORT_HELPER_FUNCTION() { } } -TEST_CASE("Prediction tests") { +TEST_CASE("Test Prediction Helpers") { TEST_SHUFFLE_HELPER_FUNCTIONS(); TEST_SORT_HELPER_FUNCTION(); diff --git a/tests/cpp-tests/results/TestResults.cpp b/tests/cpp-tests/results/TestResults.cpp index a847c126..acacea48 100644 --- a/tests/cpp-tests/results/TestResults.cpp +++ b/tests/cpp-tests/results/TestResults.cpp @@ -37,9 +37,10 @@ void TEST_SETTERS_AND_GETTERS() { REQUIRE(results_instacne->GetTotalModelingFlops() == 1.0); }SECTION("Avg Modeling Execution Time Setter/Getter") { + results_instacne->SetMLEIterations(0); REQUIRE_THROWS(results_instacne->GetAverageModelingExecutionTime()); - results_instacne->SetMLEIterations(2.0); + results_instacne->SetMLEIterations(2); results_instacne->SetTotalModelingExecutionTime(4.0); REQUIRE(results_instacne->GetAverageModelingExecutionTime() == 2.0); @@ -47,7 +48,7 @@ void TEST_SETTERS_AND_GETTERS() { results_instacne->SetMLEIterations(0); REQUIRE_THROWS(results_instacne->GetAverageModelingFlops()); - results_instacne->SetMLEIterations(2.0); + results_instacne->SetMLEIterations(2); results_instacne->SetTotalModelingFlops(4.0); REQUIRE(results_instacne->GetAverageModelingFlops() == 2.0); } From e7b412c05ec156375ab596e8e1fad603e59e40a9 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 2 Apr 2024 11:34:45 +0200 Subject: [PATCH 45/82] full R --- CHANGELOG.md | 3 +- README.md | 13 +- USER_MANUAL.md | 112 ++----- cleanup | 28 -- docs/ExaGeoStatCPP-handout.png | Bin 0 -> 748012 bytes examples/data-loader/CSVLoader.cpp | 2 +- examples/descriptors/ChameleonDescriptor.cpp | 4 +- .../descriptors/ChameleonToHicmaConverter.cpp | 8 +- examples/end-to-end/DataModeling.cpp | 3 +- examples/end-to-end/DataPrediction.cpp | 3 +- examples/hardware/ExaGeoStatHardware.cpp | 5 +- .../Rcpp-adapters/FunctionsAdapter.hpp | 259 +++++++++++----- inst/include/api/ExaGeoStat.hpp | 19 +- inst/include/common/Definitions.hpp | 20 +- inst/include/common/PluginRegistry.hpp | 12 +- .../include/configurations/Configurations.hpp | 8 +- .../include/data-generators/DataGenerator.hpp | 1 + .../data-generators/LocationGenerator.hpp | 6 +- inst/include/data-loader/DataLoader.hpp | 5 +- .../data-loader/concrete/CSVLoader.hpp | 1 - inst/include/data-units/DescriptorData.hpp | 24 +- inst/include/data-units/ExaGeoStatData.hpp | 15 +- inst/include/data-units/Locations.hpp | 1 + .../data-units/ModelingDataHolders.hpp | 6 +- .../descriptor/ExaGeoStatDescriptor.hpp | 2 - .../concrete/ChameleonDescriptor.hpp | 1 - .../descriptor/concrete/HicmaDescriptor.hpp | 1 - inst/include/hardware/ExaGeoStatHardware.hpp | 24 +- inst/include/helpers/BasselFunction.hpp | 48 +-- inst/include/helpers/CommunicatorMPI.hpp | 2 + .../helpers/DistanceCalculationHelpers.hpp | 4 + inst/include/kernels/Kernel.hpp | 11 +- .../concrete/BivariateMaternFlexible.hpp | 2 + .../concrete/BivariateMaternParsimonious.hpp | 2 + .../BivariateSpacetimeMaternStationary.hpp | 2 + .../concrete/TrivariateMaternParsimonious.hpp | 2 + .../concrete/UnivariateExpNonGaussian.hpp | 2 + .../concrete/UnivariateMaternDbeta.hpp | 2 + .../concrete/UnivariateMaternDdbetaBeta.hpp | 2 + .../concrete/UnivariateMaternDdbetaNu.hpp | 2 + .../concrete/UnivariateMaternDdnuNu.hpp | 2 + .../UnivariateMaternDdsigmaSquare.hpp | 2 + .../UnivariateMaternDdsigmaSquareBeta.hpp | 2 + .../UnivariateMaternDdsigmaSquareNu.hpp | 2 + .../kernels/concrete/UnivariateMaternDnu.hpp | 2 + .../concrete/UnivariateMaternDsigmaSquare.hpp | 2 + .../concrete/UnivariateMaternNonGaussian.hpp | 2 + .../UnivariateMaternNuggetsStationary.hpp | 2 + .../concrete/UnivariateMaternStationary.hpp | 2 + .../concrete/UnivariatePowExpStationary.hpp | 2 + .../UnivariateSpacetimeMaternStationary.hpp | 2 + .../LinearAlgebraMethods.hpp | 16 +- .../chameleon/ChameleonImplementation.hpp | 5 + .../chameleon/dense/ChameleonDense.hpp | 3 + inst/include/prediction/Prediction.hpp | 22 +- .../PredictionAuxiliaryFunctions.hpp | 1 + inst/include/prediction/PredictionHelpers.hpp | 7 + inst/include/results/Results.hpp | 40 ++- inst/include/runtime/RuntimeFunctions.hpp | 12 +- .../runtime/starpu/concrete/dmdet-codelet.hpp | 4 +- .../starpu/concrete/dtrace-codelet.hpp | 2 +- .../non-gaussian-transform-codelet.hpp | 6 +- .../helpers/concrete/HicmaStarPuHelpers.hpp | 2 +- inst/include/utilities/EnumStringParser.hpp | 20 +- inst/include/utilities/ErrorHandler.hpp | 50 +-- man/Data.Rd | 8 +- man/Hardware.Rd | 2 +- man/fisher.Rd | 35 +-- man/get_Z_measurement_vector.Rd | 3 +- man/get_locationsX.Rd | 3 +- man/get_locationsY.Rd | 1 - man/get_locationsZ.Rd | 1 - man/idw.Rd | 27 +- man/mloe_mmom.Rd | 28 +- man/model_data.Rd | 12 +- man/predict_data.Rd | 23 +- man/simulate_data.Rd | 9 +- scripts/Benchmarking.sh | 8 +- src/Rcpp-adapters/FunctionsAdapter.cpp | 284 ++++++++---------- src/Rcpp-adapters/RcppExports.cpp | 4 +- src/Rcpp-adapters/RcppModules.cpp | 20 +- src/api/ExaGeoStat.cpp | 5 +- src/configurations/Configurations.cpp | 8 +- src/data-generators/DataGenerator.cpp | 2 - src/data-generators/LocationGenerator.cpp | 12 +- .../concrete/SyntheticGenerator.cpp | 20 +- src/data-loader/DataLoader.cpp | 2 - src/data-loader/concrete/CSVLoader.cpp | 3 - src/data-units/DescriptorData.cpp | 16 +- src/data-units/Locations.cpp | 3 - .../descriptor/ExaGeoStatDescriptor.cpp | 5 +- .../descriptor/concrete/HicmaDescriptor.cpp | 3 +- src/hardware/ExaGeoStatHardware.cpp | 19 +- src/helpers/CommunicatorMPI.cpp | 8 +- .../concrete/UnivariateMaternDdnuNu.cpp | 25 +- src/kernels/concrete/UnivariateMaternDnu.cpp | 3 +- .../concrete/UnivariatePowExpStationary.cpp | 3 +- .../LinearAlgebraMethods.cpp | 42 +-- .../chameleon/ChameleonImplementation.cpp | 12 +- .../concrete/tlr/HicmaImplementation.cpp | 22 +- src/prediction/Prediction.cpp | 22 +- src/runtime/CMakeLists.txt | 2 +- src/runtime/parsec/CMakeLists.txt | 2 +- src/runtime/starpu/CMakeLists.txt | 6 +- src/runtime/starpu/concrete/dcmg-codelet.cpp | 10 +- src/runtime/starpu/concrete/ddotp-codelet.cpp | 4 +- src/runtime/starpu/concrete/dmdet-codelet.cpp | 28 +- .../starpu/concrete/dmloe-mmom-codelet.cpp | 19 +- src/runtime/starpu/concrete/dmse-codelet.cpp | 6 +- .../starpu/concrete/dtrace-codelet.cpp | 2 +- .../concrete/gaussian-to-non-codelet.cpp | 8 +- .../concrete/non-gaussian-loglike-codelet.cpp | 6 +- src/runtime/starpu/helpers/CMakeLists.txt | 2 +- tests/cpp-tests/CMakeLists.txt | 2 +- .../Rcpp-adapters/TestAllRFunctions.cpp | 226 +++++++------- tests/cpp-tests/api/TestExaGeoStatApi.cpp | 15 +- .../configurations/TestConfigurations.cpp | 39 +-- .../concrete/TestCSVDataGenerator.cpp | 78 ++--- .../concrete/TestSyntheticGenerator.cpp | 24 +- .../data-units/TestDescriptorData.cpp | 28 +- tests/cpp-tests/hardware/CMakeLists.txt | 2 +- tests/cpp-tests/helpers/CMakeLists.txt | 2 +- tests/cpp-tests/helpers/TestDiskWriter.cpp | 6 +- .../concrete/TestBivariateMaternFlexible.cpp | 2 +- .../TestBivariateMaternParsimonious.cpp | 4 +- .../TestTrivariateMaternParsimonious.cpp | 2 +- .../TestUnivariateMaternNonGaussian.cpp | 4 +- .../TestUnivariateMaternNuggetsStationary.cpp | 2 +- .../TestUnivariateMaternStationary.cpp | 2 +- .../TestUnivariatePowExpStationary.cpp | 4 +- .../concrete/TestHiCMAImplementationTLR.cpp | 8 +- tests/cpp-tests/prediction/CMakeLists.txt | 2 +- tests/cpp-tests/prediction/TestPrediction.cpp | 35 ++- tests/cpp-tests/results/CMakeLists.txt | 2 +- tests/cpp-tests/results/TestResults.cpp | 4 +- tests/heavy-tests/ExamplesTests.cpp | 174 +++++++---- tests/heavy-tests/HeavyTests.cpp | 202 +++++++------ tests/heavy-tests/README.md | 3 +- 138 files changed, 1362 insertions(+), 1187 deletions(-) delete mode 100755 cleanup create mode 100644 docs/ExaGeoStatCPP-handout.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 41fc89f0..5a9fd42a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] - Version 1.0.1 (YYYY-MM-DD) +## [Unreleased] - Version 1.1.0 (YYYY-MM-DD) ### Added - Implemented a new changelog. - Introduced a benchmarking script. - .gitignore file. - More examples. - Add tests for all src files. +- Rcpp support. ### Fixed - Resolved issues with MPI installation. diff --git a/README.md b/README.md index f92e2f03..5b2b18be 100644 --- a/README.md +++ b/README.md @@ -109,12 +109,13 @@ Project Hierarchy * **```docs```** A directory contains all the necessary documents. * **```examples```** A directory contains a comprehensive collection of demo code that illustrates the framework's application and demonstrates its features and capabilities. * **```inst```** A directory contains all the system's header files, mirroring the structure of the src directory. -* **```prerequisites```** A directory contains all the necessary prerequisites for the project and default scripts for their installation. +* **```man```** A directory contains all the R functions documentation. +* **```scripts```** A directory contains benchmarking scripts. * **```src```** A directory contains all the source files. * **```tests```** A directory contains all the test files and follows the same structure as the src folder. * **```clean_build.sh```** A script is designed to compile the software tests once all dependencies are installed, and it is set to build everything by default. * **```CMakeLists.txt```** The top-level CMake file to configure the build system. -* **```config.sh```** A Script used to generate the building system inside a 'bin' directory. +* **```configure```** A Script used to generate the building system inside a 'bin' directory. Installation ============ @@ -135,9 +136,9 @@ To install the `ExaGeoStat` project locally, run the following commands in your `cd exageostatcpp` -3. Run `config.sh` script with the flag `-h` for help, to know the supported options and their corresponding flags. +3. Run `configure` script with the flag `-h` for help, to know the supported options and their corresponding flags. - `./config.sh -h` + `./configure -h` or check user manual. @@ -164,7 +165,6 @@ Run the following commands in your terminal: 2. Open the R prompt window by simply running `R` command in the terminal - 3. Inside the prompt, we will install needed packages by running the following commads: - `install.packages(Rcpp)` @@ -177,7 +177,7 @@ Run the following commands in your terminal: 4. Return to the terminal and run the following command, make sure your current path is the ExaGeoStat project directory - `R CMD INSTALL . --configure-args="r"` + `R CMD INSTALL . --configure-args="-r -e"` > This command installs our ExaGeoStat code as a package to be able to run it using R. We pass `-r`to the configuration to enable usage of R @@ -237,3 +237,4 @@ References and Energy Consumption of Geospatial Modeling Applications Using Automated Precision Conversion." In 2023 IEEE International Conference on Cluster Computing (CLUSTER), IEEE, 2023. +![ExaGeoStatCPP-handout.png](docs%2FExaGeoStatCPP-handout.png) \ No newline at end of file diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 128849c0..87e03ff7 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -13,10 +13,10 @@ ExaGeoStatCPP User Manual ## Configurations -* Run the help of `config.sh` to know the needed arguments for your specific options. +* Run the help of `configure` to know the needed arguments for your specific options. ```commandline -./config.sh -h +./configure -h ``` * To Enable support of HiCMA, add `-H` disabled by default. @@ -29,6 +29,8 @@ ExaGeoStatCPP User Manual * To change the installation path of the dependencies, use `-i ` project_path/installdir/_deps/ by default on Unix systems. * To enable manually passing mkl as BLA vendor, add `--use-mkl` MKL by default. * To enable packaging system for distribution, add `-p` disabled by default. +* To enable showing code warnings, add `-w` disabled by default. +* To manually set mkl as blas vendor, add `--use-mkl`. MKL is required as blas vendor and it's automatically detected but in some environments it need to be manually set. ## Building @@ -241,7 +243,7 @@ The subsequent arguments are as follows: - `number of gpus`: Specifies the number of GPUs to be used for the solver. -##### *ExaGeoStatR wrapper* +##### *ExaGeoStat R wrapper* ```R hardware <- new(Hardware, computation, number of cores, number of gpus); hardware$finalize_hardware() @@ -273,25 +275,15 @@ Here we use already existing data by providing the path to it: - The Data Path must be passed to Configuration ``` ---log_path= +data_path <- ``` And then using the following code: -```c++ -// Create a new ExaGeoStat data that holds the locations data and descriptors data. -std::unique_ptr> data; +```R +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension, data_path=data_path) -// Generate data by passing your arguments through the configurations, your hardware and your container of the data which will be filled with the new generated data. -ExaGeoStat::ExaGeoStatLoadData(hardware, configurations, data); ``` -The subsequent arguments are as follows: - - - `kernel`: Specifies the kernel function to be used in the simulation. - - `initial_theta`: Sets the initial values for the parameters of the simulation. - - `problem_size`: Defines the size of the problem or dataset to be simulated. - - `dts`: Specifies the time steps or intervals for the simulation. - - `dimension`: Indicates the dimensionality of the data to be simulated. ### Location Getters @@ -301,7 +293,7 @@ ExaGeoStat supports locations data of dimension upto 3D, and therefore we have g ```c++ double *locations_x = exageostat_data->GetLocations()->GetLocationX(); ``` -##### *ExaGeoStatR wrapper* +##### *ExaGeoStat R wrapper* ```R locations_x <- get_locationsX(data=exageostat_data) ``` @@ -313,7 +305,7 @@ The subsequent arguments are as follows: ```c++ double *locations_y = exageostat_data->GetLocations()->GetLocationY(); ``` -##### *ExaGeoStatR wrapper* +##### *ExaGeoStat R wrapper* ```R locations_y <- get_locationsY(data=exageostat_data) ``` @@ -325,7 +317,7 @@ The subsequent arguments are as follows: ```c++ double *locations_z = exageostat_data->GetLocations()->GetLocationZ(); ``` -##### *ExaGeoStatR wrapper* +##### *ExaGeoStat R wrapper* ```R locations_x <- get_locationsZ(data=exageostat_data) ``` @@ -347,7 +339,7 @@ The used variables are as follows: - `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data. - `desc_Z_values`: pointer to descriptor matrix. -##### *ExaGeoStatR wrapper* +##### *ExaGeoStat R wrapper* ```R desc_Z_values <- get_Z_measurement_vector(data=exageostat_data, type="chameleon") ``` @@ -365,24 +357,14 @@ ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data, z_mat ``` -##### *ExaGeoStatR wrapper* +##### *ExaGeoStat R wrapper* ```R -theta <- model_data(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, dimension=dimension, lb=lower_bound, ub=upper_bound, mle_itr=10, computation=computation) +estimated_theta <- model_data(matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10, computation=computation, band=1) +``` +Or +```R +estimated_theta <- model_data(data=exageostat_data, kernel=kernel, dts=dts, dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10) ``` -This function models data based on specified parameters: - - - `data`: The dataset to be used for modeling. - - `matrix`: The matrix of values to be used in the model. - - `x`: The x-coordinates of the data points. - - `y`: The y-coordinates of the data points. - - `kernel`: The kernel function to be applied in the model. - - `dts`: The time steps or intervals for the model. - - `dimension`: The dimensionality of the data. - - `lb`: The lower bound for the model parameters. - - `ub`: The upper bound for the model parameters. - - `mle_itr`: The number of iterations for maximum likelihood estimation. - - `computation`: The computation mode for the model. - ### Data Prediction ```c++ @@ -391,20 +373,10 @@ ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); ``` -##### *ExaGeoStatR wrapper* +##### *ExaGeoStat R wrapper* ```R -predict_data(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, z_miss=5) +predict_data(train_data=list(locations_x, locations_y, locations_z, z_value), test_data=list(test_x, test_y, test_z), kernel=kernel, dts=dts, estimated_theta=estimated_theta) ``` -This function predicts data based on specified parameters: - - - `data`: The dataset to be used for prediction. - - `matrix`: The matrix of values to be used in the prediction. - - `x`: The x-coordinates of the data points. - - `y`: The y-coordinates of the data points. - - `kernel`: The kernel function to be applied in the prediction. - - `dts`: The time steps or intervals for the prediction. - - `estimated_theta`: The estimated parameters from the model. - - `z_miss`: The number of missing values to be handled in the prediction. ### Fisher Function 1. Pass the fisher arguments to the Configurations. @@ -419,20 +391,10 @@ ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); ``` -##### *ExaGeoStatR wrapper* +##### *ExaGeoStat R wrapper* ```R -fisher(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, z_miss=5) +fisher_matrix <- fisher(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) ``` -This function predicts data based on specified parameters: - - - `data`: The dataset to be used for the Fisher method. - - `matrix`: The matrix of values to be used in the Fisher method. - - `x`: The x-coordinates of the data points. - - `y`: The y-coordinates of the data points. - - `kernel`: The kernel function to be applied in the Fisher method. - - `dts`: The time steps or intervals for the Fisher method. - - `estimated_theta`: The initial estimated parameters for the Fisher method. - - `z_miss`: The number of missing values to be handled in the Fisher method. ### MLOE-MMOM Function 1. Pass the MLOE-MMOM arguments to the Configurations. @@ -448,21 +410,10 @@ ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); ``` -##### *ExaGeoStatR wrapper* +##### *ExaGeoStat R wrapper* ```R -mloe_mmom(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, true_theta=estimated_theta, z_miss=5) +result_mloe_mmom = mloe_mmom(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta, true_theta=true_theta) ``` -This function predicts data based on specified parameters: - - - `data`: The dataset to be used for the MLE. - - `matrix`: The matrix of values to be used in the MLE. - - `x`: The x-coordinates of the data points. - - `y`: The y-coordinates of the data points. - - `kernel`: The kernel function to be applied in the MLE. - - `dts`: The time steps or intervals for the MLE. - - `estimated_theta`: The initial estimated parameters for the MLE. - - `true_theta`: The true parameters for the MLE, used for comparison or validation. - - `z_miss`: The number of missing values to be handled in the MLE. ### IDW Function 1. Pass the IDW arguments to the Configurations. @@ -478,21 +429,10 @@ ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); ``` -##### *ExaGeoStatR wrapper* +##### *ExaGeoStat R wrapper* ```R -idw(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, z_miss=5) +idw_error = idw(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta, test_measurements=test_measurements) ``` -This function predicts data based on specified parameters: - - - `data`: The dataset to be used for the IDW. - - `matrix`: The matrix of values to be used in the IDW. - - `x`: The x-coordinates of the data points. - - `y`: The y-coordinates of the data points. - - `kernel`: The kernel function to be applied in the IDW. - - `dts`: The time steps or intervals for the IDW. - - `estimated_theta`: The initial estimated parameters for the IDW. - - `z_miss`: The number of missing values to be handled in the IDW. - ## Contributing [Contribution Guidelines](CONTRIBUTING.md) - \ No newline at end of file diff --git a/cleanup b/cleanup deleted file mode 100755 index 01ed4d6d..00000000 --- a/cleanup +++ /dev/null @@ -1,28 +0,0 @@ -#! /bin/sh - -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, -# All rights reserved. -# ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). - -# @file cleanup -# @brief This file is intended to be used during 'R CMD build' command to delete all unnecessary files to match to CRAN policies. -# @version 1.1.0 -# @author David Helmy -# @author Mahmoud Elkarargy -# @date 2024-02-09 - -rm -rf ./src/*.so* -rm -rf ./src/*.d -rm -rf ./src/*.dll -rm -rf ./.idea/ -rm -rf ./cmake-build-debug -rm -rf ./bin/ -rm -rf ./.git/ -rm -rf ./config.sh -rm -rf ./clean_build.sh -rm -rf ./..Rcheck/ -rm -rf ./benchmarks/ -rm -rf ./doxygen_config -rm -rf ./prerequisites/ -rm -rf ./scripts/ -rm -rf ./installdir/ diff --git a/docs/ExaGeoStatCPP-handout.png b/docs/ExaGeoStatCPP-handout.png new file mode 100644 index 0000000000000000000000000000000000000000..1bd401bcc80dda12375de4165a8784a72616c83c GIT binary patch literal 748012 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 PUlZbdYpfS8Y^urDefyr35K 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
jiit$DnT7dT}&eE-UcDGbo5f8F_uEJh* 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

nY1Tl05V!DY9|LFI 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 z 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%0D<3DPOLI`;zw_jB3|-Jlt&8p|WA9 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 literal 0 HcmV?d00001 diff --git a/examples/data-loader/CSVLoader.cpp b/examples/data-loader/CSVLoader.cpp index 0f04119c..fed01c80 100644 --- a/examples/data-loader/CSVLoader.cpp +++ b/examples/data-loader/CSVLoader.cpp @@ -66,7 +66,7 @@ int main(int argc, char **argv) { // Print loaded data LOGGER("Data Loaded:") - for (int i=0;imat; for (int i = 0; i < config_problem_size; ++i) { - LOGGER_PRECISION_1(" " << data_mat[i],0) + LOGGER_PRECISION_1(" " << data_mat[i], 0) } delete kernel; diff --git a/examples/descriptors/ChameleonToHicmaConverter.cpp b/examples/descriptors/ChameleonToHicmaConverter.cpp index 624e131b..0b1ceed3 100644 --- a/examples/descriptors/ChameleonToHicmaConverter.cpp +++ b/examples/descriptors/ChameleonToHicmaConverter.cpp @@ -92,10 +92,10 @@ int main(int argc, char **argv) { // Print Data Matrix of Descriptor LOGGER("** Data in Matrix of Chameleon descriptor:") - LOGGER_2("",0) + LOGGER_2("", 0) auto *cham_mat = (double *) CHAM_descriptorC->mat; for (int i = 0; i < config_problem_size; ++i) { - LOGGER_PRECISION_1(" " << cham_mat[i],0) + LOGGER_PRECISION_1(" " << cham_mat[i], 0) } auto *HICMA_descriptorC = data->ConvertChameleonToHicma(CHAM_descriptorC); @@ -119,9 +119,9 @@ int main(int argc, char **argv) { // Print Data Matrix of Descriptor LOGGER("** Data in Matrix of Hicma descriptor:") auto *hicma_mat = (double *) HICMA_descriptorC->mat; - LOGGER_2("",0) + LOGGER_2("", 0) for (int i = 0; i < config_problem_size; ++i) { - LOGGER_PRECISION_1(" " << hicma_mat[i],0) + LOGGER_PRECISION_1(" " << hicma_mat[i], 0) } delete kernel; diff --git a/examples/end-to-end/DataModeling.cpp b/examples/end-to-end/DataModeling.cpp index 9c30608c..14a559f9 100644 --- a/examples/end-to-end/DataModeling.cpp +++ b/examples/end-to-end/DataModeling.cpp @@ -47,7 +47,8 @@ int main(int argc, char **argv) { configurations.GetGPUsNumbers()); //Data Setup - std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>( + configurations.GetProblemSize(), configurations.GetDimension()); // Initiating the matrix of the CHAMELEON Descriptor Z. auto *z_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, diff --git a/examples/end-to-end/DataPrediction.cpp b/examples/end-to-end/DataPrediction.cpp index 23e51879..e7ea846a 100644 --- a/examples/end-to-end/DataPrediction.cpp +++ b/examples/end-to-end/DataPrediction.cpp @@ -46,7 +46,8 @@ int main(int argc, char **argv) { configurations.GetGPUsNumbers()); //Data Setup - std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>( + configurations.GetProblemSize(), configurations.GetDimension()); //creating locations x and y. auto *location_x = new double[N]{0.193041886015106440, 0.330556191348134576, 0.181612878614480805, diff --git a/examples/hardware/ExaGeoStatHardware.cpp b/examples/hardware/ExaGeoStatHardware.cpp index 81f1c1ef..3372d435 100644 --- a/examples/hardware/ExaGeoStatHardware.cpp +++ b/examples/hardware/ExaGeoStatHardware.cpp @@ -32,10 +32,11 @@ int main(int argc, char **argv) { // Initialize Configuration Configurations configuration; - configuration.InitializeArguments(argc,argv); + configuration.InitializeArguments(argc, argv); // Initialize Hardware - auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), configuration.GetGPUsNumbers()); + auto hardware = ExaGeoStatHardware(configuration.GetComputation(), configuration.GetCoresNumber(), + configuration.GetGPUsNumbers()); return 0; } \ No newline at end of file diff --git a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp index 936695c5..bfa42154 100644 --- a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp +++ b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp @@ -16,63 +16,39 @@ #define EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP #include -#include -#include -#include #include #include namespace exageostat::adapters { - /** - * @brief Initializes and configures the R arguments for ExaGeoStat computations. - * @details This function prepares the necessary configurations required by ExaGeoStat to perform statistical computations. - * It includes setting up problem sizes, computational kernels, grid configurations, and other parameters essential - * for the execution of the ExaGeoStat algorithms. - * @param[in] aProblemSize The size of the problem to be solved. - * @param[in] aKernelName The name of the computational kernel to be used. - * @param[in] aTileSize A vector specifying the size of each tile in the computation. - * @param[in] aP_QGrid A vector defining the P x Q process grid. - * @param[in] aTimeSlot The time slot allocated for the computation. - * @param[in] aComputation The type of computation to be performed (e.g., estimation, prediction). - * @param[in] aPrecision The precision (e.g., single, double) of the computation. - * @param[in] aCoresGPUsNumber A vector specifying the number of CPU cores or GPUs to be used. - * @param[in] aBand The bandwidth of the problem, relevant for band-limited computations. - * @param[in] aMaxRank The maximum rank for low-rank approximations. - * @param[in] aInitialTheta A vector of initial values for the model parameters (theta). - * @param[in] aLowerUpperBounds A 2D vector specifying the lower and upper bounds for model parameters. - * @param[in] aEstimatedTheta A vector of estimated values for the model parameters after computation. - * @param[in] aVerbose A string indicating the verbosity level of the output. - * @param[in] aDimension The dimensionality of the problem (e.g., 2D, 3D). - * @param[in] aMaxMleIterations The maximum number of iterations for the Maximum Likelihood Estimation (MLE) algorithm. - * @param[in] aTolerance The tolerance threshold for convergence in iterative algorithms. - * @param[in] aPrediction A vector indicating prediction locations or settings. - * @return A pointer to a Configurations object containing the initialized settings. - * - */ - /** * @brief Retrieves X coordinates of locations from ExaGeoStat data. - * @details Extracts and returns the X coordinates of geographical or spatial locations stored in an ExaGeoStatData object, facilitating data manipulation and analysis within the ExaGeoStat framework. + * @details Extracts and returns the X coordinates of geographical or spatial locations stored in an ExaGeoStatData object, + * facilitating data manipulation and analysis within the ExaGeoStat framework. * @param[in] apData Pointer to ExaGeoStatData object containing the spatial data. * @return Numeric vector of X coordinates. + * */ Rcpp::NumericVector R_GetLocationX(ExaGeoStatData *apData); /** * @brief Retrieves Y coordinates of locations from ExaGeoStat data. - * @details Extracts and returns the Y coordinates of geographical or spatial locations stored in an ExaGeoStatData object, supporting various spatial data analyses and operations within the ExaGeoStat software. + * @details Extracts and returns the Y coordinates of geographical or spatial locations stored in an ExaGeoStatData object, + * supporting various spatial data analyses and operations within the ExaGeoStat software. * @param[in] apData Pointer to ExaGeoStatData object containing the spatial data. * @return Numeric vector of Y coordinates. + * */ Rcpp::NumericVector R_GetLocationY(ExaGeoStatData *apData); /** * @brief Retrieves Z coordinates of locations from ExaGeoStat data. - * @details Extracts and returns the Z coordinates (elevation or depth) of spatial locations stored in an ExaGeoStatData object, enhancing three-dimensional spatial analysis capabilities within the ExaGeoStat framework. + * @details Extracts and returns the Z coordinates (elevation or depth) of spatial locations stored in an ExaGeoStatData object, + * enhancing three-dimensional spatial analysis capabilities within the ExaGeoStat framework. * @param[in] apData Pointer to ExaGeoStatData object containing the spatial data. * @return Numeric vector of Z coordinates. + * */ Rcpp::NumericVector R_GetLocationZ(ExaGeoStatData *apData); @@ -82,6 +58,7 @@ namespace exageostat::adapters { * @param[in] apData Pointer to ExaGeoStatData object containing the spatial data. * @param[in] aType String specifying the type of descriptor value to retrieve (e.g., "Chameleon", "HiCMA"). * @return Numeric vector of descriptive Z values. + * */ Rcpp::NumericVector R_GetDescZValues(ExaGeoStatData *apData, const std::string &aType); @@ -89,7 +66,18 @@ namespace exageostat::adapters { * @brief Function to load ExaGeoStat data. * @details This function loads data into an ExaGeoStatData object using the provided configuration and computational settings. * It is designed to initialize the data structure necessary for subsequent statistical model operations within the ExaGeoStat framework. - * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data will be stored. + * @param[in] aKernelName Name of the computational kernel to be utilized. + * @param[in] aInitialTheta Initial parameter values for the statistical model. + * @param[in] aDistanceMatrix Type of distance matrix to be used ("euclidean", "manhattan", etc.). + * @param[in] aProblemSize Size of the problem or dataset. + * @param[in] aSeed Seed for random number generation, ensuring reproducibility. + * @param[in] aDenseTileSize Size of the tile for dense computations. + * @param[in] aLowTileSize Size of the tile for low-rank computations. + * @param[in] aDimension Dimensionality of the problem ("2D" for two dimensions, "3D" for three dimensions). + * @param[in] aLogPath Path to the log file where execution details will be stored. + * @param[in] aDataPath Path to the data file containing spatial observations. + * @param[in] aRecoveryFilePath Path for saving intermediate computation states, aiding in recovery from interruptions. + * @param[in] aObservationsFilePath Path to the file containing observation data. * @return A pointer to an ExaGeoStatData object containing the loaded data. * */ @@ -101,70 +89,193 @@ namespace exageostat::adapters { const std::string &aRecoveryFilePath, const std::string &aObservationsFilePath); /** - * @brief Function to model ExaGeoStat data. - * @details This function applies the model configurations specified in apConfigurations to the data stored in apData. - * It prepares the ExaGeoStatData object for statistical analysis and prediction tasks, adjusting the internal data representations and parameters as needed. - * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data is stored and will be modeled. - * @param[in] aMeasurementsVector An optional Rcpp::Nullable object containing a vector of measurements. If provided, it is used to enhance the data modeling process. - * @param[in] aLocationsX An optional Rcpp::Nullable object containing a vector of X coordinates for the locations. If provided, it is used to enhance the data modeling process. - * @param[in] aLocationsY An optional Rcpp::Nullable object containing a vector of Y coordinates for the locations. If provided, it is used to enhance the data modeling process. - * @param[in] aLocationsZ An optional Rcpp::Nullable object containing a vector of Z coordinates for the locations. If provided, it is used to enhance the data modeling process. - * @return A pointer to an ExaGeoStatData object containing the modeled data, ready for statistical analysis and prediction. - */ + * @brief Models ExaGeoStat data using specified arguments. + * @details Applies statistical modeling to ExaGeoStatData based on the provided configurations. + * This function is essential for preparing the data for in-depth statistical analysis and predictions, + * optimizing internal representations and parameters for the modeling process. + * @param[in] aComputation Computational method to be used. + * @param[in] aKernelName Name of the kernel for computations. + * @param[in] aDistanceMatrix Type of distance matrix ("euclidean", "manhattan", etc.). + * @param[in] aLowerBound Lower bound for optimization parameters. + * @param[in] aUpperBound Upper bound for optimization parameters. + * @param[in] aTolerance Tolerance level for the optimization algorithm. + * @param[in] aMleIterations Maximum number of iterations for the Maximum Likelihood Estimation (MLE) algorithm. + * @param[in] aDenseTileSize Tile size for dense matrix computations. + * @param[in] aLowTileSize Tile size for low-rank approximations. + * @param[in] aDimension Dimensionality of the problem ("2D" or "3D"). + * @param[in] aBand Bandwidth for band matrices, applicable in certain computational kernels. + * @param[in] aMaxRank Maximum rank for low-rank approximations. + * @param[in] apData Pointer to ExaGeoStatData object to be modeled. + * @param[in] aMeasurementsVector Optional vector of measurements to enhance modeling, can be nullable. + * @param[in] aLocationsX Optional vector of X coordinates for locations, can be nullable. + * @param[in] aLocationsY Optional vector of Y coordinates for locations, can be nullable. + * @param[in] aLocationsZ Optional vector of Z coordinates for locations, can be nullable. + * @return Vector of doubles representing the modeled theta. + * + */ std::vector R_ExaGeoStatModelData(const std::string &aComputation, const std::string &aKernelName, const std::string &aDistanceMatrix, const std::vector &aLowerBound, const std::vector &aUpperBound, const int &aTolerance, const int &aMleIterations, const int &aDenseTileSize, - const int &aLowTileSize, const std::string &aDimension, const int &aBand, const int &aMaxRank, - SEXP apData, + const int &aLowTileSize, const std::string &aDimension, const int &aBand, + const int &aMaxRank, SEXP apData, Rcpp::Nullable aMeasurementsVector = R_NilValue, Rcpp::Nullable aLocationsX = R_NilValue, Rcpp::Nullable aLocationsY = R_NilValue, Rcpp::Nullable aLocationsZ = R_NilValue); /** - * @brief Function to predict using ExaGeoStat data. - * @details This function performs predictions based on the modeled ExaGeoStatData object. - * It utilizes the configuration and computational settings defined by apConfigurations, respectively, to execute prediction algorithms on the data stored in apData. - * @param[in] apConfigurations A pointer to a Configurations object containing the computational settings. - * @param[in] apData A pointer to an ExaGeoStatData object where the loaded data is stored and will be modeled. - * @param[in] aMeasurementsVector An optional Rcpp::Nullable object containing a vector of measurements. If provided, it is used to enhance the data modeling process. - * @param[in] aLocationsX An optional Rcpp::Nullable object containing a vector of X coordinates for the locations. If provided, it is used to enhance the data modeling process. - * @param[in] aLocationsY An optional Rcpp::Nullable object containing a vector of Y coordinates for the locations. If provided, it is used to enhance the data modeling process. - * @param[in] aLocationsZ An optional Rcpp::Nullable object containing a vector of Z coordinates for the locations. If provided, it is used to enhance the data modeling process. - * @return A pointer to an ExaGeoStatData object containing the modeled data, ready for statistical analysis and prediction. + * @brief Predicts outcomes using ExaGeoStat data and configurations. + * @details Utilizes a modeled ExaGeoStatData object to perform predictions, leveraging specified computational settings and statistical models. + * This function is integral for generating spatial predictions based on the data and models within the ExaGeoStat framework. + * @param[in] aKernelName Name of the kernel used for prediction computations. + * @param[in] aDistanceMatrix Type of distance matrix used ("euclidean", "manhattan", etc.). + * @param[in] aEstimatedTheta Vector of estimated parameters from the model. + * @param[in] aDenseTileSize Tile size for dense matrix operations. + * @param[in] aLowTileSize Tile size for low-rank matrix operations. + * @param[in] aDimension Dimensionality of the spatial data ("2D" or "3D"). + * @param[in] aTrainData Training data set used for predictions. + * @param[in] aTestData Test data set for which predictions are made. + * @return Vector of predicted values based on the test data. + * */ std::vector R_ExaGeoStatPredictData(const std::string &aKernelName, const std::string &aDistanceMatrix, - const std::vector &aEstimatedTheta, - const int &aDenseTileSize, const int &aLowTileSize, - const std::string &aDimension, std::vector> &aTrainData, - std::vector> &aTestData); + const std::vector &aEstimatedTheta, const int &aDenseTileSize, + const int &aLowTileSize, const std::string &aDimension, + std::vector> &aTrainData, + std::vector> &aTestData); + /** + * @brief Calculates the Mean Logarithmic Error (MLOE) and the Mean Measure of Model Output (MMOM) for ExaGeoStat predictions. + * @details Assesses the accuracy of spatial predictions made by the ExaGeoStat framework by computing the MLOE and MMOM, + * which provide insights into the predictive performance and uncertainty of the models. + * @param[in] aKernelName Kernel used for the prediction computations. + * @param[in] aDistanceMatrix Type of distance matrix ("euclidean", "manhattan", etc.). + * @param[in] aEstimatedTheta Vector of estimated parameters from the model. + * @param[in] aTrueTheta Vector of true parameter values for validation. + * @param[in] aDenseTileSize Tile size for dense matrix operations. + * @param[in] aLowTileSize Tile size for low-rank matrix operations. + * @param[in] aDimension Dimensionality of the spatial data ("2D" or "3D"). + * @param[in] aTrainData Training data set used in the model. + * @param[in] aTestData Test data set used for validation. + * @return Vector containing the calculated MLOE and MMOM values. + * + */ std::vector R_ExaGeoStatMLOE_MMOM(const std::string &aKernelName, const std::string &aDistanceMatrix, - const std::vector &aEstimatedTheta, - const std::vector &aTrueTheta, - const int &aDenseTileSize, const int &aLowTileSize, - const std::string &aDimension, std::vector> &aTrainData, + const std::vector &aEstimatedTheta, + const std::vector &aTrueTheta, const int &aDenseTileSize, + const int &aLowTileSize, const std::string &aDimension, + std::vector> &aTrainData, std::vector> &aTestData); + /** + * @brief Computes the Fisher information matrix for ExaGeoStat models. + * @details Utilizes the estimated parameters and the Fisher information matrix to evaluate the information content and + * parameter uncertainties within the ExaGeoStat framework, contributing to the understanding of model reliability and sensitivity. + * @param[in] aKernelName Kernel used for computations. + * @param[in] aDistanceMatrix Type of distance matrix ("euclidean", "manhattan", etc.). + * @param[in] aEstimatedTheta Vector of estimated parameters from the model. + * @param[in] aDenseTileSize Tile size for dense matrix operations. + * @param[in] aLowTileSize Tile size for low-rank matrix operations. + * @param[in] aDimension Dimensionality of the spatial data ("2D" or "3D"). + * @param[in] aTrainData Training data set used in the model. + * @param[in] aTestData Test data set used for validation. + * @return Vector representing the Fisher information matrix. + * + */ std::vector R_ExaGeoStatFisher(const std::string &aKernelName, const std::string &aDistanceMatrix, - const std::vector &aEstimatedTheta, - const int &aDenseTileSize, - const int &aLowTileSize, const std::string &aDimension, + const std::vector &aEstimatedTheta, const int &aDenseTileSize, + const int &aLowTileSize, const std::string &aDimension, std::vector> &aTrainData, std::vector> &aTestData); + /** + * @brief Applies Inverse Distance Weighting (IDW) for spatial interpolation using ExaGeoStat data. + * @details Implements the IDW interpolation method to estimate spatial variables at unsampled locations based on the distances + * and values of nearby sampled points within the ExaGeoStat framework, enhancing spatial prediction capabilities. + * @param[in] aKernelName Kernel used for IDW computations. + * @param[in] aDistanceMatrix Type of distance matrix ("euclidean", "manhattan", etc.). + * @param[in] aEstimatedTheta Vector of parameters, typically used for weighting in IDW. + * @param[in] aDenseTileSize Tile size for dense matrix operations. + * @param[in] aLowTileSize Tile size for low-rank matrix operations. + * @param[in] aDimension Dimensionality of the spatial data ("2D" or "3D"). + * @param[in] aTrainData Training data set providing sampled locations and values. + * @param[in] aTestData Test data set providing unsampled locations for which values are interpolated. + * @param[in] aTestMeasurementsValues Vector of measured values at the test locations, used as reference in some IDW implementations. + * @return Vector of interpolated values at the test locations. + * + */ std::vector R_ExaGeoStatIDW(const std::string &aKernelName, const std::string &aDistanceMatrix, - const std::vector &aEstimatedTheta, - const int &aDenseTileSize, const int &aLowTileSize, - const std::string &aDimension, + const std::vector &aEstimatedTheta, const int &aDenseTileSize, + const int &aLowTileSize, const std::string &aDimension, std::vector> &aTrainData, - std::vector> &aTestData, std::vector &aTestMeasurementsValues); + std::vector> &aTestData, + std::vector &aTestMeasurementsValues); - double *GetDataFromArguments(Rcpp::Nullable aMeasurementsVector, Rcpp::Nullable aLocationsX,Rcpp::Nullable aLocationsY, Rcpp::Nullable aLocationsZ, std::unique_ptr> &aData, configurations::Configurations &aConfigurations, - const std::string &aKernelName, const std::string &aDistanceMatrix, - const int &aDenseTileSize, const int &aLowTileSize, const std::string &aDimension, const common::Computation &aComputation); + /** + * @brief Extracts and prepares data from given arguments for ExaGeoStat operations. + * @details This function is designed to parse and prepare spatial and measurement data from provided arguments, + * making it suitable for processing within the ExaGeoStat framework. It handles optional data vectors for measurements + * and locations (X, Y, Z coordinates), and configures an ExaGeoStatData object based on these inputs along with other + * computational and configuration parameters. + * @param[in] aMeasurementsVector vector of measurements to enhance modeling, can be nullable. + * @param[in] aLocationsX vector of X coordinates for locations, can be nullable. + * @param[in] aLocationsY vector of Y coordinates for locations, can be nullable. + * @param[in] aLocationsZ vector of Z coordinates for locations, can be nullable. + * @param[in] aData Pointer to ExaGeoStatData object to be modeled. + * @param[in] aConfigurations Configuration settings specifying computational details such as the kernel type, matrix storage format, etc. + * @param[in] aKernelName Name of the kernel for computations. + * @param[in] aDistanceMatrix Type of distance matrix ("euclidean", "manhattan", etc.). + * @param[in] aDenseTileSize Tile size for dense matrix computations. + * @param[in] aLowTileSize Tile size for low-rank approximations. + * @param[in] aDimension Dimensionality of the problem ("2D" or "3D"). + * @param[in] aComputation Computational method to be used. + * @return Pointer to a double array containing the prepared data, ready for use in ExaGeoStat operations. + * + */ + double *GetDataFromArguments(Rcpp::Nullable aMeasurementsVector, + Rcpp::Nullable aLocationsX, + Rcpp::Nullable aLocationsY, + Rcpp::Nullable aLocationsZ, + std::unique_ptr> &aData, + configurations::Configurations &aConfigurations, const std::string &aKernelName, + const std::string &aDistanceMatrix, const int &aDenseTileSize, const int &aLowTileSize, + const std::string &aDimension, const common::Computation &aComputation); + + /** + * @brief Validates the dimensions of input data. + * @details This function checks the dimensions of the provided data vectors to ensure they meet the expected format and size requirements for a given data type. + * It's used to verify that the data structures passed into algorithms or processes are correctly formatted, preventing errors or inconsistencies in data processing. + * @param aData A constant reference to a vector of vectors containing the data to be validated. + * @param aDataType A string describing the type of data being validated, which influences the expected dimensions and format of the data. + * @return void + * + */ + void ValidateDataDimensions(const std::vector> &aData, const std::string &aDataType); + + /** + * @brief Sets up the prediction environment. + * @details This function prepares the necessary configurations and data structures for making predictions. It involves setting up various parameters, including kernel names, distance matrices, tile sizes, dimensions, and training/test data. The function is crucial for initializing the prediction process with the appropriate settings and data. + * @param aConfigurations Reference to a Configurations object containing various prediction and algorithm configurations. + * @param aKernelName Name of the kernel to be used in predictions. + * @param aDistanceMatrix String representation of the distance matrix to be used. + * @param aDenseTileSize Size of the dense tiles in the matrix. + * @param aLowTileSize Size of the low-resolution tiles in the matrix. + * @param aDimension String representation of the dimensionality of the data. + * @param aTrainData Reference to a vector of vectors containing the training data. + * @param aTestData Reference to a vector of vectors containing the test data. + * @param aEstimatedTheta Vector containing estimated theta values for the model. + * @param aTestMeasurementsValues Vector containing the test measurement values. + * @return Pointer to a double array containing the prepared data, ready for use in ExaGeoStat operations. + * + */ + void PredictionSetupHelper(configurations::Configurations &aConfigurations, const std::string &aKernelName, + const std::string &aDistanceMatrix, const int &aDenseTileSize, const int &aLowTileSize, + const std::string &aDimension, std::vector> &aTrainData, + std::vector> &aTestData, + const std::vector &aEstimatedTheta, + const std::vector &aTestMeasurementsValues); } #endif //EXAGEOSTATCPP_FUNCTIONSADAPTER_HPP diff --git a/inst/include/api/ExaGeoStat.hpp b/inst/include/api/ExaGeoStat.hpp index 86a15c57..4bbc0590 100644 --- a/inst/include/api/ExaGeoStat.hpp +++ b/inst/include/api/ExaGeoStat.hpp @@ -16,7 +16,6 @@ #include -#include #include #include @@ -59,6 +58,7 @@ namespace exageostat::api { * @param[in] aGrad An array of length n where you can optionally return the gradient of the objective function. * @param[in] apInfo pointer containing needed configurations and data. * @return double MLE results. + * */ static double ExaGeoStatMLETileAPI(const std::vector &aTheta, std::vector &aGrad, void *apInfo); @@ -67,19 +67,16 @@ namespace exageostat::api { * @param[in] aConfigurations Reference to Configurations object containing user input data. * @param[in, out] aData Reference to an ExaGeoStatData object containing needed descriptors, and locations. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. + * @param[in] apTrainLocations (Optional) Pointer to Locations representing training locations. these are used in training phase. + * @param[in] apTestLocations (Optional) Pointer to Locations representing test locations. These are used in prediction phase. * @return void + * */ - static void ExaGeoStatPrediction(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, - T *apMeasurementsMatrix = nullptr, - dataunits::Locations *apTrainLocations = nullptr, - dataunits::Locations *apTestLocations = nullptr); + static void + ExaGeoStatPrediction(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, + T *apMeasurementsMatrix = nullptr, dataunits::Locations *apTrainLocations = nullptr, + dataunits::Locations *apTestLocations = nullptr); - private: - /** - * @brief - * Prevent Class Instantiation for API Wrapper Class. - */ - ExaGeoStat() = default; }; /** diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index 65e91a68..78fdb067 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -17,23 +17,21 @@ #ifndef EXAGEOSTATCPP_DEFINITIONS_HPP #define EXAGEOSTATCPP_DEFINITIONS_HPP -// This is a hot fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. +// This is a fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. +#ifdef USE_HICMA #ifdef min #undef min #endif +#endif -#include -#include -#include #include #include -#include /** * @def EXAGEOSTAT_INSTANTIATE_CLASS * @brief Macro definition to instantiate the EXAGEOSTAT template classes with supported types. + * **/ - #define EXAGEOSTAT_INSTANTIATE_CLASS(TEMPLATE_CLASS) template class TEMPLATE_CLASS; \ template class TEMPLATE_CLASS; @@ -93,6 +91,7 @@ namespace exageostat::common { /** * @enum Side * @brief Enum denoting the side on which the matrix appears in an equation. + * */ enum Side { EXAGEOSTAT_LEFT = 141, @@ -102,6 +101,7 @@ namespace exageostat::common { /** * @enum Trans * @brief Enum denoting whether or not to transpose a matrix. + * */ enum Trans { EXAGEOSTAT_NO_TRANS = 111, @@ -112,6 +112,7 @@ namespace exageostat::common { /** * @enum Diag * @brief Enum denoting whether the diagonal is unitary. + * */ enum Diag { EXAGEOSTAT_NON_UNIT = 131, @@ -121,6 +122,7 @@ namespace exageostat::common { /** * @enum Distance metric * @brief Enum denoting distance metric type. + * */ enum DistanceMetric { EUCLIDEAN_DISTANCE = 0, @@ -130,6 +132,7 @@ namespace exageostat::common { /** * @enum Descriptor Type * @brief Enum denoting the Descriptor Type. + * */ enum DescriptorType { CHAMELEON_DESCRIPTOR = 0, @@ -139,6 +142,7 @@ namespace exageostat::common { /** * @enum Data source Type * @brief Enum denoting the data source Type. + * */ enum DataSourceType { SYNTHETIC = 0, @@ -148,6 +152,7 @@ namespace exageostat::common { /** * @enum Descriptor Name * @brief Enum denoting all Descriptors Names. + * */ enum DescriptorName : int { DESCRIPTOR_C = 0, @@ -208,7 +213,9 @@ namespace exageostat::common { }; /** + * @enum Tile Storage * @brief Matrix tile storage + * */ typedef enum TileStorage { EXAGOSTAT_CM = 101, @@ -284,6 +291,7 @@ namespace exageostat::common { * The set is initialized with a lambda function that iterates through a directory * and extracts the kernel names from the filenames. It also adds lowercase versions * of the kernel names with underscores before each capital letter. + * @return set of all available kernels names * */ const static std::set availableKernels = []() { diff --git a/inst/include/common/PluginRegistry.hpp b/inst/include/common/PluginRegistry.hpp index f91c8fc1..bcde1187 100644 --- a/inst/include/common/PluginRegistry.hpp +++ b/inst/include/common/PluginRegistry.hpp @@ -14,15 +14,13 @@ #ifndef EXAGEOSTATCPP_PLUGINREGISTRY_HPP #define EXAGEOSTATCPP_PLUGINREGISTRY_HPP -// This is a hot fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. +// This is a fix to avoid the problem in HiCMA which set the min definition with a conflict implementation of chrono library. +#ifdef USE_HICMA #ifdef min #undef min #endif +#endif -#include -#include -#include -#include #include #include @@ -70,14 +68,14 @@ namespace exageostat::plugins { * @return A pointer to the created plugin, or nullptr if the plugin could not be created. * */ - static T *Create(const std::string &aName, const int& aTimeSlot) { + static T *Create(const std::string &aName, const int &aTimeSlot) { auto map = GetFactoryMap(); if (map.find(aName) == map.end()) { return nullptr; } // Get the object from the map. - T* object = map[aName](); + T *object = map[aName](); // Automatically set the new P value which will get updated with the user input of P. object->SetPValue(aTimeSlot); return object; diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index cc50964c..1d9b221d 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -27,7 +27,6 @@ * the specified type and sets the member variable with the specified name * to the value of the argument. The name of the member variable is used as * the key to set the corresponding value in the specified dictionary. - * * @param[in] name The name of the member variable to be set. * @param[in] type The data type of the member variable. * @param[in] argument_name The name of the argument to the generated function. @@ -44,7 +43,6 @@ void Set##name(type argument_name) \ * @brief Macro that generates a getter function for a member variable. * @details This macro generates a function named Get##name that returns the value of * the member variable with the specified name from the specified dictionary. - * * @param[in] name The name of the member variable to be retrieved. * @param[in] type The data type of the member variable. * @param[in] dictionary_name The name of the dictionary to retrieve the value from. @@ -86,6 +84,7 @@ namespace exageostat::configurations { * @param[in] apArgV The array of arguments. * @param[in] aEnableR check if R is enabled * @details This method initializes the command line arguments and set default values for unused args. + * @return void * */ void InitializeArguments(const int &aArgC, char **apArgV, const bool &aEnableR = false); @@ -93,6 +92,7 @@ namespace exageostat::configurations { /** * @brief Initialize the all theta arguments. * @return void + * */ void InitializeAllTheta(); @@ -353,6 +353,7 @@ namespace exageostat::configurations { * @brief Checks the value of the unknown observations parameter. * @param[in] aValue A string representing the number of unknown observations. * @return The corresponding integer value. + * */ int CheckUnknownObservationsValue(const std::string &aValue); @@ -369,12 +370,14 @@ namespace exageostat::configurations { * @brief print the summary of MLE inputs. * @param[in] aRank A MPI Rank variable * @return void + * */ inline void PrintSummary(int aRank = 0); /** * @brief Calculates the number of observed measurements. * @return number of observed measurements. + * */ int CalculateZObsNumber(); @@ -391,6 +394,7 @@ namespace exageostat::configurations { * @brief parse user's input to distance metric. * @param[in] aDistanceMetric string specifying the used distance metric. * @return void + * */ void ParseDistanceMetric(const std::string &aDistanceMetric); diff --git a/inst/include/data-generators/DataGenerator.hpp b/inst/include/data-generators/DataGenerator.hpp index 05b4fe9d..afe875e7 100644 --- a/inst/include/data-generators/DataGenerator.hpp +++ b/inst/include/data-generators/DataGenerator.hpp @@ -60,6 +60,7 @@ namespace exageostat::generators { virtual ~DataGenerator(); protected: + /// Used enum for data generators types. static common::DataSourceType aDataSourceType; }; diff --git a/inst/include/data-generators/LocationGenerator.hpp b/inst/include/data-generators/LocationGenerator.hpp index 023b6ba4..d2b3c7e9 100644 --- a/inst/include/data-generators/LocationGenerator.hpp +++ b/inst/include/data-generators/LocationGenerator.hpp @@ -39,7 +39,8 @@ namespace exageostat::generators { * @return void * */ - static void GenerateLocations(const int &aN, const int &aTimeSlot, const common::Dimension &aDimension, dataunits::Locations &aLocations); + static void GenerateLocations(const int &aN, const int &aTimeSlot, const common::Dimension &aDimension, + dataunits::Locations &aLocations); /** * @brief Generate uniform distribution between rangeLow , rangeHigh. @@ -58,7 +59,8 @@ namespace exageostat::generators { * @return void * */ - static void SortLocations(const int &aN, const common::Dimension &aDimension, dataunits::Locations &aLocations); + static void + SortLocations(const int &aN, const common::Dimension &aDimension, dataunits::Locations &aLocations); }; /** diff --git a/inst/include/data-loader/DataLoader.hpp b/inst/include/data-loader/DataLoader.hpp index 62c4c937..80ffe0f8 100644 --- a/inst/include/data-loader/DataLoader.hpp +++ b/inst/include/data-loader/DataLoader.hpp @@ -46,9 +46,12 @@ namespace exageostat::dataLoader { * @param aYLocations Vector to store Y coordinates of locations. * @param aZLocations Vector to store Z coordinates of locations (if applicable). * @param aP Partition index for distributed data loading. + * @return void + * */ virtual void - ReadData(configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, std::vector &aXLocations, + ReadData(configurations::Configurations &aConfigurations, std::vector &aMeasurementsMatrix, + std::vector &aXLocations, std::vector &aYLocations, std::vector &aZLocations, const int &aP) = 0; /** diff --git a/inst/include/data-loader/concrete/CSVLoader.hpp b/inst/include/data-loader/concrete/CSVLoader.hpp index d6bb924a..ce3e2de9 100644 --- a/inst/include/data-loader/concrete/CSVLoader.hpp +++ b/inst/include/data-loader/concrete/CSVLoader.hpp @@ -15,7 +15,6 @@ #ifndef EXAGEOSTAT_CPP_CSVDATALOADER_HPP #define EXAGEOSTAT_CPP_CSVDATALOADER_HPP -#include #include namespace exageostat::dataLoader::csv { diff --git a/inst/include/data-units/DescriptorData.hpp b/inst/include/data-units/DescriptorData.hpp index 7cc64c37..fe9061f4 100644 --- a/inst/include/data-units/DescriptorData.hpp +++ b/inst/include/data-units/DescriptorData.hpp @@ -15,6 +15,8 @@ #ifndef EXAGEOSTATCPP_DESCRIPTORDATA_HPP #define EXAGEOSTATCPP_DESCRIPTORDATA_HPP +#include + #include #include @@ -23,6 +25,7 @@ namespace exageostat::dataunits { /** * @brief Union representing the base descriptor. * @details This union is used to store different types of descriptors based on the configuration. + * */ union BaseDescriptor { CHAM_desc_t *chameleon_desc; @@ -35,6 +38,7 @@ namespace exageostat::dataunits { * @Class DescriptorData * @brief Manages geo-statistical descriptor data with functions for retrieving and manipulating descriptors * @tparam T Data Type: float or double + * */ template class DescriptorData { @@ -42,12 +46,14 @@ namespace exageostat::dataunits { public: /** * @brief Default Constructor for DescriptorData. + * */ explicit DescriptorData() = default; /** * @brief Destructor for DescriptorData. + * */ ~DescriptorData(); @@ -57,6 +63,7 @@ namespace exageostat::dataunits { * @param[in] aDescriptorName The name of the descriptor. * @return The base descriptor. * @throws std::runtime_error if the corresponding library is not enabled (USE_HICMA). + * */ BaseDescriptor GetDescriptor(const common::DescriptorType &aDescriptorType, const common::DescriptorName &aDescriptorName); @@ -71,7 +78,7 @@ namespace exageostat::dataunits { /** * @brief Set the sequence. * @param[in] apSequence Pointer to the sequence. - * + * @return void */ void SetSequence(void *apSequence); @@ -85,6 +92,7 @@ namespace exageostat::dataunits { /** * @brief Set the request. * @param[in] apRequest Pointer to the request. + * @return void * */ void SetRequest(void *apRequest); @@ -95,6 +103,7 @@ namespace exageostat::dataunits { * @brief Converts a CHAMELEON descriptor to a HICMA descriptor. * @param[in] apChameleonDesc Pointer to the CHAMELEON descriptor to be converted. * @return Pointer to the converted HICMA descriptor. + * */ HICMA_desc_t *ConvertChameleonToHicma(CHAM_desc_t *apChameleonDesc); @@ -121,30 +130,36 @@ namespace exageostat::dataunits { * @param[in] aValidOOC Boolean refer to whether this descriptor can be created with OOC technology or not, default is true * @return void * @throws std::runtime_error if the corresponding library is not enabled (USE_HICMA). + * */ void SetDescriptor(const common::DescriptorType &aDescriptorType, const common::DescriptorName &aDescriptorName, const bool &aIsOOC, void *apMatrix, const common::FloatPoint &aFloatPoint, const int &aMB, const int &aNB, const int &aSize, const int &aLM, const int &aLN, const int &aI, - const int &aJ, const int &aM, const int &aN, const int &aP, const int &aQ, const bool &aValidOOC = true); + const int &aJ, const int &aM, const int &aN, const int &aP, const int &aQ, + const bool &aValidOOC = true); /** * @brief Getter for the Descriptor matrix. * @param[in] aDescriptorType Type of the descriptor, whether it's CHAMELEON or HiCMA. - * @param[in] apDescriptor Pointer to the descriptor. + * @param[in] aDescriptorName The name of the descriptor. * @return pointer to the Descriptor matrix. * @throws std::runtime_error if the corresponding library is not enabled (USE_HICMA). + * */ - T *GetDescriptorMatrix(const common::DescriptorType &aDescriptorType, void *apDescriptor); + T *GetDescriptorMatrix(const common::DescriptorType &aDescriptorType, + const common::DescriptorName &aDescriptorName); /** * @brief Getter for the mIsDescriptorInitiated field. * @return mIsDescriptorInitiated + * */ bool GetIsDescriptorInitiated(); /** * @brief Setter for mIsDescriptorInitiated field. * @param aIsInitiated Boolean for setting the mIsDescriptorInitiated field. + * */ void SetIsDescriptorInitiated(bool aIsInitiated); @@ -154,6 +169,7 @@ namespace exageostat::dataunits { * @param[in] aDescriptorName The descriptor name. * @return The descriptor name as a string. * @throws std::invalid_argument if the provided descriptor name is not available. + * */ std::string GetDescriptorName(const common::DescriptorName &aDescriptorName); diff --git a/inst/include/data-units/ExaGeoStatData.hpp b/inst/include/data-units/ExaGeoStatData.hpp index 14a02ec6..29389eb2 100644 --- a/inst/include/data-units/ExaGeoStatData.hpp +++ b/inst/include/data-units/ExaGeoStatData.hpp @@ -17,8 +17,6 @@ #include #include -#include -#include /** * @Class ExaGeoStatData @@ -33,6 +31,7 @@ class ExaGeoStatData { * @brief Constructor for ExaGeoStatData. * @param[in] aSize The size of the data. * @param[in] aDimension The dimension of the data. + * */ ExaGeoStatData(const int &aSize, const exageostat::common::Dimension &aDimension); @@ -40,34 +39,41 @@ class ExaGeoStatData { * @brief Constructor for ExaGeoStatData. * @param[in] aSize The size of the data. * @param[in] aDimension The dimension of the data. + * */ ExaGeoStatData(const int &aSize, const std::string &aDimension); /** * @brief Default constructor for ExaGeoStatData. + * */ ExaGeoStatData() = default; /** * @brief Destructor for ExaGeoStatData. + * */ ~ExaGeoStatData(); /** * @brief Get the locations. * @return Pointer to the Locations object. + * */ exageostat::dataunits::Locations *GetLocations(); /** * @brief Set the locations. * @param[in] aLocation Pointer to the Locations object. + * @return void + * */ void SetLocations(exageostat::dataunits::Locations &aLocation); /** * @brief Get the descriptor data. * @return Pointer to the DescriptorData object. + * */ exageostat::dataunits::DescriptorData *GetDescriptorData(); @@ -75,12 +81,14 @@ class ExaGeoStatData { * @brief Setter for the number of performed MLE iterations. * @param[in] aMleIterations number of performed MLE iterations. * @return void + * */ void SetMleIterations(const int &aMleIterations); /** * @brief Get the number of performed MLE iterations. * @return Pointer to the DescriptorData object. + * */ int GetMleIterations(); @@ -89,9 +97,9 @@ class ExaGeoStatData { * @param[in] aKernelName Name of the Kernel used. * @param[out] aLocations Location object to save medianLocations in. * @return void + * */ void CalculateMedianLocations(const std::string &aKernelName, exageostat::dataunits::Locations &aLocations); - using MyTemplateClassDouble = ExaGeoStatData; private: //// Used descriptor data. @@ -101,6 +109,7 @@ class ExaGeoStatData { //// Current number of performed MLE iterations. int mMleIterations = 0; }; + /** * @brief Instantiates the ExaGeoStatData class for float and double types. * @tparam T Data Type: float or double diff --git a/inst/include/data-units/Locations.hpp b/inst/include/data-units/Locations.hpp index 43519585..3d9d7f61 100644 --- a/inst/include/data-units/Locations.hpp +++ b/inst/include/data-units/Locations.hpp @@ -39,6 +39,7 @@ namespace exageostat::dataunits { /** * @brief Default copy constructor. * @param[in] aLocations Locations to be copied. + * */ Locations(const Locations &aLocations) = default; diff --git a/inst/include/data-units/ModelingDataHolders.hpp b/inst/include/data-units/ModelingDataHolders.hpp index 3eadd519..bc9b8f15 100644 --- a/inst/include/data-units/ModelingDataHolders.hpp +++ b/inst/include/data-units/ModelingDataHolders.hpp @@ -33,8 +33,10 @@ namespace exageostat::dataunits { * @param aConfiguration The Configurations object. * @param aKernel The Kernel object. */ - mModelingData(std::unique_ptr> &aData, configurations::Configurations &aConfiguration, T &aMatrix, const kernels::Kernel &aKernel) : - mpData(std::move(&aData)), mpConfiguration(&aConfiguration), mpMeasurementsMatrix(&aMatrix), mpKernel(&aKernel) {} + mModelingData(std::unique_ptr> &aData, configurations::Configurations &aConfiguration, + T &aMatrix, const kernels::Kernel &aKernel) : + mpData(std::move(&aData)), mpConfiguration(&aConfiguration), mpMeasurementsMatrix(&aMatrix), + mpKernel(&aKernel) {} }; }//namespace exageostat diff --git a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp index 48f3de04..eb075807 100644 --- a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp +++ b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp @@ -15,8 +15,6 @@ #ifndef EXAGEOSTATCPP_EXAGEOSTATDESCRIPTOR_HPP #define EXAGEOSTATCPP_EXAGEOSTATDESCRIPTOR_HPP -#include - #include #include #include diff --git a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp index 35be4d45..b5ebf4ea 100644 --- a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp @@ -16,7 +16,6 @@ #define EXAGEOSTATCPP_CHAMELEONDESCRIPTOR_HPP #include - #include namespace exageostat::dataunits::descriptor { diff --git a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp index 9a34b820..0575a87d 100644 --- a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp @@ -16,7 +16,6 @@ #define EXAGEOSTATCPP_HICMADESCRIPTOR_HPP #include - #include namespace exageostat::dataunits::descriptor { diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index 549f7a17..5e08faa7 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -19,6 +19,7 @@ /** * @brief Class representing the hardware configuration for the ExaGeoStat solver. + * */ class ExaGeoStatHardware { @@ -30,24 +31,28 @@ class ExaGeoStatHardware { * @param[in] aGpuNumber The number of GPUs to use for the solver. * */ - explicit ExaGeoStatHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); + explicit ExaGeoStatHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, + const int &aGpuNumber); /** * @brief Constructor for ExaGeoStatHardware. * @param[in] aComputation The computation mode for the solver as a string. * @param[in] aCoreNumber The number of CPU cores to use for the solver. * @param[in] aGpuNumber The number of GPUs to use for the solver. + * */ explicit ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber); /** * @brief A Finalize caller for Hardware. * @return void. + * */ void FinalizeHardware(); /** * @brief Destructor for ExaGeoStatHardware. + * */ ~ExaGeoStatHardware(); @@ -56,8 +61,11 @@ class ExaGeoStatHardware { * @param[in] aComputation The computation mode for the solver. * @param[in] aCoreNumber The number of CPU cores to use for the solver. * @param[in] aGpuNumber The number of GPUs to use for the solver. + * @return void + * */ - static void InitHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); + static void + InitHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); /** * @brief Get the Chameleon hardware context. @@ -81,11 +89,11 @@ class ExaGeoStatHardware { */ [[nodiscard]] static void *GetContext(exageostat::common::Computation aComputation); - private: - //// Used Pointer to the Chameleon hardware context. - static void *mpChameleonContext; - //// Used Pointer to the Hicma hardware context. - static void *mpHicmaContext; - }; +private: + //// Used Pointer to the Chameleon hardware context. + static void *mpChameleonContext; + //// Used Pointer to the Hicma hardware context. + static void *mpHicmaContext; +}; #endif // EXAGEOSTATCPP_EXAGEOSTATHARDWARE_HPP \ No newline at end of file diff --git a/inst/include/helpers/BasselFunction.hpp b/inst/include/helpers/BasselFunction.hpp index 541a6974..c7d2a7b2 100644 --- a/inst/include/helpers/BasselFunction.hpp +++ b/inst/include/helpers/BasselFunction.hpp @@ -29,32 +29,32 @@ namespace exageostat::helpers { class BasselFunction { public: - /** - * @brief Calculates the derivative of the modified Bessel function of the second kind (K_nu) with respect to its order, evaluated at input_value and order aOrder. - * @param[in] aOrder The order of the Bessel function. - * @param[in] aInputValue The input value at which to evaluate the derivative. - * @return The value of the derivative of K_nu with respect to its order, evaluated at input_value and order aOrder. - * - */ - static T CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue); + /** + * @brief Calculates the derivative of the modified Bessel function of the second kind (K_nu) with respect to its order, evaluated at input_value and order aOrder. + * @param[in] aOrder The order of the Bessel function. + * @param[in] aInputValue The input value at which to evaluate the derivative. + * @return The value of the derivative of K_nu with respect to its order, evaluated at input_value and order aOrder. + * + */ + static T CalculateDerivativeBesselNu(const T &aOrder, const T &aInputValue); - /** - * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. - * @param[in] aOrder The order of the Bessel function. - * @param[in] aInputValue The input value at which to evaluate the second derivative. - * @return The value of the second derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. - * - */ - static T CalculateSecondDerivativeBesselNu(const T &aOrder, const T &aInputValue); + /** + * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. + * @param[in] aOrder The order of the Bessel function. + * @param[in] aInputValue The input value at which to evaluate the second derivative. + * @return The value of the second derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. + * + */ + static T CalculateSecondDerivativeBesselNu(const T &aOrder, const T &aInputValue); - /** - * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. - * @param[in] aOrder The order of the Bessel function. - * @param[in] aInputValue The input value at which to evaluate the derivative. - * @return The value of the derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. - * - */ - static T CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aInputValue); + /** + * @brief Calculates the second derivative of the modified Bessel function of the second kind (K_nu) with respect to its input, evaluated at input_value and order aOrder. + * @param[in] aOrder The order of the Bessel function. + * @param[in] aInputValue The input value at which to evaluate the derivative. + * @return The value of the derivative of K_nu with respect to its input, evaluated at input_value and order aOrder. + * + */ + static T CalculateSecondDerivativeBesselNuInput(const T &aOrder, const T &aInputValue); }; diff --git a/inst/include/helpers/CommunicatorMPI.hpp b/inst/include/helpers/CommunicatorMPI.hpp index 23162c58..b3aa9245 100644 --- a/inst/include/helpers/CommunicatorMPI.hpp +++ b/inst/include/helpers/CommunicatorMPI.hpp @@ -27,6 +27,7 @@ namespace exageostat::helpers { /** * @brief Get a pointer to the singleton instance of the CommunicatorMPI class. * @return A pointer to the instance of the CommunicatorMPI class. + * */ static CommunicatorMPI *GetInstance(); @@ -57,6 +58,7 @@ namespace exageostat::helpers { /** * @brief Prevent Class Instantiation for Communicator MPI Class. + * */ CommunicatorMPI() = default; diff --git a/inst/include/helpers/DistanceCalculationHelpers.hpp b/inst/include/helpers/DistanceCalculationHelpers.hpp index e035995a..61e443cd 100644 --- a/inst/include/helpers/DistanceCalculationHelpers.hpp +++ b/inst/include/helpers/DistanceCalculationHelpers.hpp @@ -23,6 +23,7 @@ namespace exageostat::helpers { * @Class DistanceCalculationHelpers * @brief Class to calculate the distance between two points. * @tparam T Data Type: float or double. + * */ template @@ -37,6 +38,7 @@ namespace exageostat::helpers { * @param[in] aDistanceMetric Flag indicating the distance metric to use (1 for Manhattan distance, 2 for Euclidean distance). * @param[in] aFlagZ Flag indicating whether the points are in 2D or 3D space (0 for 2D, 1 for 3D). * @return The Euclidean distance between the two points. + * */ static T CalculateDistance(exageostat::dataunits::Locations &aLocations1, exageostat::dataunits::Locations &aLocations2, const int &aIdxLocation1, @@ -49,6 +51,7 @@ namespace exageostat::helpers { * @param[in] aLatitude2 Latitude of the second point in degrees. * @param[in] aLongitude2 Longitude of the second point in degrees. * @return The distance between the two points in kilometers. + * */ static T DistanceEarth(T &aLatitude1, T &aLongitude1, T &aLatitude2, T &aLongitude2); @@ -57,6 +60,7 @@ namespace exageostat::helpers { * @details This function converts an angle from degrees to radians using the conversion factor π/180. * @param[in] aDegree The angle in degrees. * @return The angle converted to radians. + * */ static T DegreeToRadian(T aDegree); diff --git a/inst/include/kernels/Kernel.hpp b/inst/include/kernels/Kernel.hpp index dabb3261..65e23aab 100644 --- a/inst/include/kernels/Kernel.hpp +++ b/inst/include/kernels/Kernel.hpp @@ -40,15 +40,6 @@ extern "C" { */ #define EARTH_RADIUS 6371.0 -/** - * @def POW_e - * @brief The value of e to the power of 1. - * @details This macro defines the value of e to the power of 1, which is used in some kernel functions. - * - */ -#define POW_e (2.71828182845904) - - namespace exageostat::kernels { struct KernelsConfigurations { @@ -78,6 +69,7 @@ namespace exageostat::kernels { /** * Default virtual destructor to be overridden by the the suitable concrete kernel destructor. + * */ virtual ~Kernel() = default; @@ -94,6 +86,7 @@ namespace exageostat::kernels { * @param[in] aLocalTheta An array of kernel parameters. * @param [in] aDistanceMetric Distance metric to be used (1 = Euclidean, 2 = Manhattan, 3 = Minkowski). * @return void + * */ virtual void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/BivariateMaternFlexible.hpp b/inst/include/kernels/concrete/BivariateMaternFlexible.hpp index e2688090..ebdf3ed8 100644 --- a/inst/include/kernels/concrete/BivariateMaternFlexible.hpp +++ b/inst/include/kernels/concrete/BivariateMaternFlexible.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new BivariateMaternFlexible object. * @details Initializes a new BivariateMaternFlexible object with default values. + * */ BivariateMaternFlexible(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp b/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp index 05226389..2ff6b0d5 100644 --- a/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp +++ b/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new BivariateMaternParsimonious object. * @details Initializes a new BivariateMaternParsimonious object with default values. + * */ BivariateMaternParsimonious(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp b/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp index 9a0b8dc0..a32670e7 100644 --- a/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp +++ b/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new BivariateSpacetimeMaternStationary object. * @details Initializes a new BivariateSpacetimeMaternStationary object with default values. + * */ BivariateSpacetimeMaternStationary(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp b/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp index 4aa525c8..53c4cad9 100644 --- a/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp +++ b/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new TrivariateMaternParsimonious object. * @details Initializes a new TrivariateMaternParsimonious object with default values. + * */ TrivariateMaternParsimonious(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp b/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp index 3afaf163..905433ed 100644 --- a/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp +++ b/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateExpNonGaussian object. * @details Initializes a new UnivariateExpNonGaussian object with default values. + * */ UnivariateExpNonGaussian(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp index 3866b690..21836598 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternDbeta object. * @details Initializes a new UnivariateMaternDbeta object with default values. + * */ UnivariateMaternDbeta(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp index 6ac9e051..001dacfd 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternDdbetaBeta object. * @details Initializes a new UnivariateMaternDdbetaBeta object with default values. + * */ UnivariateMaternDdbetaBeta(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp index ae53d4c6..081d8932 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternDdbetaNu object. * @details Initializes a new UnivariateMaternDdbetaNu object with default values. + * */ UnivariateMaternDdbetaNu(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp index 29678d14..722f3d59 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternDdnuNu object. * @details Initializes a new UnivariateMaternDdnuNu object with default values. + * */ UnivariateMaternDdnuNu(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp index 619705bc..c6513f30 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp @@ -41,6 +41,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternDdsigmaSquare object. * @details Initializes a new UnivariateMaternDdsigmaSquare object with default values. + * */ UnivariateMaternDdsigmaSquare(); @@ -53,6 +54,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp index 37606bf7..125cd7ca 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternDdsigmaSquareBeta object. * @details Initializes a new UnivariateMaternDdsigmaSquareBeta object with default values. + * */ UnivariateMaternDdsigmaSquareBeta(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp index c443b001..9e23ca2f 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternDdsigmaSquareNu object. * @details Initializes a new UnivariateMaternDdsigmaSquareNu object with default values. + * */ UnivariateMaternDdsigmaSquareNu(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternDnu.hpp b/inst/include/kernels/concrete/UnivariateMaternDnu.hpp index d10ba4e0..c8779765 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDnu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDnu.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternDnu object. * @details Initializes a new UnivariateMaternDnu object with default values. + * */ UnivariateMaternDnu(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp b/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp index eade1268..0e9a74c8 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternDsigmaSquare object. * @details Initializes a new UnivariateMaternDsigmaSquare object with default values. + * */ UnivariateMaternDsigmaSquare(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp b/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp index b2e638af..d582faf3 100644 --- a/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternNonGaussian object. * @details Initializes a new UnivariateMaternNonGaussian object with default values. + * */ UnivariateMaternNonGaussian(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp index 52febd31..d0e377bc 100644 --- a/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternNuggetsStationary object. * @details Initializes a new UnivariateMaternNuggetsStationary object with default values. + * */ UnivariateMaternNuggetsStationary(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateMaternStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternStationary.hpp index 18040d86..e0cc2cdc 100644 --- a/inst/include/kernels/concrete/UnivariateMaternStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternStationary.hpp @@ -41,6 +41,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateMaternStationary object. * @details Initializes a new UnivariateMaternStationary object with default values. + * */ UnivariateMaternStationary(); @@ -53,6 +54,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp b/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp index a62f861b..68b3f57f 100644 --- a/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp +++ b/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp @@ -41,6 +41,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariatePowExpStationary object. * @details Initializes a new UnivariatePowExpStationary object with default values. + * */ UnivariatePowExpStationary(); @@ -53,6 +54,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp b/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp index 6ea8d77a..946fe095 100644 --- a/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp @@ -36,6 +36,7 @@ namespace exageostat::kernels { /** * @brief Constructs a new UnivariateSpacetimeMaternStationary object. * @details Initializes a new UnivariateSpacetimeMaternStationary object with default values. + * */ UnivariateSpacetimeMaternStationary(); @@ -48,6 +49,7 @@ namespace exageostat::kernels { /** * @brief Generates a covariance matrix using a set of locations and kernel parameters. * @copydoc Kernel::GenerateCovarianceMatrix() + * */ void GenerateCovarianceMatrix(T *apMatrixA, const int &aRowsNumber, const int &aColumnsNumber, const int &aRowOffset, diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp index 347a55c3..e8135665 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp @@ -179,6 +179,7 @@ namespace exageostat::linearAlgebra { * @param[in] aMaxRank Maximum rank parameter. * @param[in] aAcc Accuracy parameter. * @return void + * */ virtual void ExaGeoStatPotrfTile(const common::UpperLower &aUpperLower, void *apA, int aBand, void *apCD, void *apCrk, @@ -197,6 +198,7 @@ namespace exageostat::linearAlgebra { * @param[in, out] apZ The matrix B of dimension, on exit is overwritten by the solution matrix X. * @param[in] aMaxRank Maximum rank parameter. * @return void + * */ virtual void ExaGeoStatTrsmTile(const common::Side &aSide, const common::UpperLower &aUpperLower, const common::Trans &aTrans, const common::Diag &aDiag, const T &aAlpha, @@ -209,6 +211,7 @@ namespace exageostat::linearAlgebra { * @param [in] apA coefficient matrix of the system of linear equations. This matrix is expected to be positive definite. * @param [in] apB Pointer to coefficient matrix of the system of linear equations. This matrix is expected to be positive definite. * @return void + * */ void ExaGeoStatPosvTile(const common::UpperLower &aUpperLower, void *apA, void *apB); @@ -226,6 +229,7 @@ namespace exageostat::linearAlgebra { * @param[in] aObsLocations Reference to Locations object containing observed locations. * @param[in] aKernel Reference to the kernel object to use. * @return the prediction Mean Square Error (MSPE). + * */ T *ExaGeoStatMLEPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, @@ -249,6 +253,7 @@ namespace exageostat::linearAlgebra { * @param[in] aObsLocations Reference to Locations object containing observed locations. * @param[in] aKernel Reference to the kernel object to use. * @return the prediction Mean Square Error (MSPE). + * */ T *ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr> &aData, T *apTheta, const int &aZMissNumber, @@ -265,6 +270,7 @@ namespace exageostat::linearAlgebra { * @param[out] apDescA Matrix Descriptor. * @param[in] aUpperLower Specifies Specifies whether the upper or lower triangular part of the covariance matrix is stored. * @return void + * */ void ExaGeoStatLap2Desc(T *apA, const int &aLDA, void *apDescA, const common::UpperLower &aUpperLower); @@ -275,6 +281,7 @@ namespace exageostat::linearAlgebra { * @param[in] apDescA Matrix Descriptor * @param[in] aUpperLower Specifies whether the upper or lower triangular part of the covariance matrix is stored. * @return void + * */ void ExaGeoStatDesc2Lap(T *apA, const int &aLDA, void *apDescA, const common::UpperLower &aUpperLower); @@ -287,6 +294,7 @@ namespace exageostat::linearAlgebra { * @param[in] aSize Size of matrix to be copied. * @param[in] aDirection Specifies the type of Descriptors to be copied. * @return void + * */ void CopyDescriptors(void *apSourceDesc, void *apDestinationDesc, const int &aSize, const common::CopyDirection &aDirection); @@ -300,6 +308,7 @@ namespace exageostat::linearAlgebra { * @param[in] aBeta All the diagonal array elements are set to aBeta. * @param[out] apDescriptor Pointer to matrix descriptor to be set with aAlpha and aBeta. * @return void + * */ void ExaGeoStatLaSetTile(const common::UpperLower &aUpperLower, T aAlpha, T aBeta, void *apDescriptor); @@ -313,7 +322,8 @@ namespace exageostat::linearAlgebra { * */ void ExaGeoStatGetZObs(configurations::Configurations &aConfigurations, T *apZ, const int &aSize, - exageostat::dataunits::DescriptorData &aDescData, T *apMeasurementsMatrix, const int &aP); + exageostat::dataunits::DescriptorData &aDescData, T *apMeasurementsMatrix, + const int &aP); /** * @brief Predict missing values based on a set of given values and covariance matrix. @@ -327,6 +337,7 @@ namespace exageostat::linearAlgebra { * @param[in] aObsLocations Locations of observed values. * @param[in] aKernel Reference to the kernel object to use. * @return void + * */ void ExaGeoStatMLETileMLOEMMOM(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, @@ -340,6 +351,7 @@ namespace exageostat::linearAlgebra { * @param[in] apTheta Pointer containing three parameter (Variance, Range, Smoothness) that is used to to generate the Covariance Matrix. * @param[in] aKernel Reference to the kernel object to use. * @return Fisher Matrix + * */ T * ExaGeoStatFisherTile(configurations::Configurations &aConfigurations, @@ -355,6 +367,7 @@ namespace exageostat::linearAlgebra { * @param[in] aBeta Scaling factor for matrix B. * @param[in] apDescB Descriptor for matrix B. * @return void + * */ void ExaGeoStatGeaddTile(const common::Trans &aTrans, const T &aAlpha, void *apDescA, const T &aBeta, void *apDescB); @@ -369,6 +382,7 @@ namespace exageostat::linearAlgebra { * @param[in] alpha Scaling factor for the multiplication. * @param[in] apDescA Descriptor for matrix A. * @param[in] apDescB Descriptor for matrix B. + * */ void ExaGeoStatTrmmTile(const common::Side &aSide, const common::UpperLower &aUpperLower, const common::Trans &aTrans, const common::Diag &aDiag, const T &alpha, void *apDescA, diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp index 994f1a06..a13f4082 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp @@ -32,6 +32,7 @@ namespace exageostat::linearAlgebra { /** * @brief Calculates the log likelihood value of a given value theta. * @copydoc LinearAlgebraMethods::ExaGeoStatMLETile() + * */ T ExaGeoStatMLETile(std::unique_ptr> &aData, configurations::Configurations &aConfigurations, const double *theta, @@ -40,12 +41,14 @@ namespace exageostat::linearAlgebra { /** * @brief Copies a matrix in the tile layout from source to destination * @copydoc LinearAlgebraMethods::ExaGeoStatLapackCopyTile() + * */ void ExaGeoStatLapackCopyTile(const common::UpperLower &aUpperLower, void *apA, void *apB) override; /** * @brief Solves one of the matrix equations op( A )*X = alpha*B, or X*op( A ) = alpha*B. * @copydoc LinearAlgebraMethods::ExaGeoStatTrsmTile() + * */ void ExaGeoStatTrsmTile(const common::Side &aSide, const common::UpperLower &aUpperLower, const common::Trans &aTrans, const common::Diag &aDiag, const T &aAlpha, void *apA, @@ -54,6 +57,7 @@ namespace exageostat::linearAlgebra { /** * @brief Wait for the completion of a sequence. * @copydoc LinearAlgebraMethods::ExaGeoStatSequenceWait() + * */ void ExaGeoStatSequenceWait(void *apSequence) override; @@ -61,6 +65,7 @@ namespace exageostat::linearAlgebra { /** * @brief Create CHAMELEON Sequence. * @copydoc LinearAlgebraMethods::ExaGeoStatCreateSequence() + * */ void ExaGeoStatCreateSequence(void *apSequence) override; diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp index fc373763..ac84ce12 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp @@ -31,17 +31,20 @@ namespace exageostat::linearAlgebra::dense { /** * @brief Default constructor. + * */ explicit ChameleonDense() = default; /** * @brief Virtual destructor to allow calls to the correct concrete destructor. + * */ ~ChameleonDense() override = default; /** * @brief Computes the Cholesky factorization of a symmetric positive definite or Symmetric positive definite matrix. * @copydoc LinearAlgebraMethods::ExaGeoStatPotrfTile() + * */ void ExaGeoStatPotrfTile(const common::UpperLower &aUpperLower, void *apA, int aBand, void *apCD, void *apCrk, diff --git a/inst/include/prediction/Prediction.hpp b/inst/include/prediction/Prediction.hpp index ae87ddad..f69d1265 100644 --- a/inst/include/prediction/Prediction.hpp +++ b/inst/include/prediction/Prediction.hpp @@ -23,8 +23,8 @@ namespace exageostat::prediction { * @Class Prediction * @brief Class to handle different Prediction Module calls. * @tparam T Data Type: float or double. + * */ - template class Prediction { public: @@ -35,12 +35,16 @@ namespace exageostat::prediction { * @param[in] aConfigurations Reference to Configurations object containing user input data. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. * @param[in] aKernel Reference to the kernel object to use. - * @return + * @param[in] apTrainLocations (Optional) Pointer to Locations representing training locations. these are used in training phase. + * @param[in] apTestLocations (Optional) Pointer to Locations representing test locations. These are used in prediction phase. + * @return void + * */ - static void PredictMissingData(std::unique_ptr> &aData, configurations::Configurations &aConfigurations, - T *apMeasurementsMatrix, const kernels::Kernel &aKernel, - dataunits::Locations *apTrainLocations = nullptr, - dataunits::Locations *apTestLocations = nullptr); + static void + PredictMissingData(std::unique_ptr> &aData, configurations::Configurations &aConfigurations, + T *apMeasurementsMatrix, const kernels::Kernel &aKernel, + dataunits::Locations *apTrainLocations = nullptr, + dataunits::Locations *apTestLocations = nullptr); /** * @brief Initializes needed pointers for prediction. @@ -53,10 +57,14 @@ namespace exageostat::prediction { * @param[out] aObsLocation Location object to be filled with missed locations. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. * @param[in] aP the P value of the kernel multiplied by time slot. + * @param[in] apTrainLocations (Optional) Pointer to Locations representing training locations. these are used in training phase. + * @param[in] apTestLocations (Optional) Pointer to Locations representing test locations. These are used in prediction phase. * @return void + * */ static void - InitializePredictionArguments(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, + InitializePredictionArguments(configurations::Configurations &aConfigurations, + std::unique_ptr> &aData, std::unique_ptr> &aLinearAlgebraSolver, T *apZObs, T *apZActual, exageostat::dataunits::Locations &aMissLocation, exageostat::dataunits::Locations &aObsLocation, T *apMeasurementsMatrix, diff --git a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp index c39c3d4b..40e65cbb 100644 --- a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp +++ b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp @@ -40,6 +40,7 @@ namespace exageostat::prediction { * @param[in] aObsLocation Reference to the observed locations. * @param[out] apMSPE Pointer to be filled with MSPE value. * @return T Array provides insight into the accuracy of the IDW-interpolated predictions for missing values + * */ static void PredictIDW(T *apZMiss, T *apZActual, T *apZObs, const int &aZMissNumber, const int &aZObsNumber, exageostat::dataunits::Locations &aMissLocation, diff --git a/inst/include/prediction/PredictionHelpers.hpp b/inst/include/prediction/PredictionHelpers.hpp index 195a5e9c..3bd1b954 100644 --- a/inst/include/prediction/PredictionHelpers.hpp +++ b/inst/include/prediction/PredictionHelpers.hpp @@ -29,6 +29,7 @@ namespace exageostat::prediction { template class PredictionHelpers { public: + /** * @brief Pick random Z points for prediction depending on p. * @param[in] aConfigurations Configurations object containing relevant settings. @@ -40,6 +41,7 @@ namespace exageostat::prediction { * @param[out] aObsLocation Location object to be filled with missed locations. * @param[in] aP the P value of the kernel multiplied by time slot. * @return void + * */ static void PickRandomPoints(configurations::Configurations &aConfigurations, std::unique_ptr> &aData, T *apZObs, T *apZActual, T *apZ, @@ -52,6 +54,7 @@ namespace exageostat::prediction { * @param[in, out] aLocations Locations to be shuffled. * @param[out] aSize Size of data. * @return void + * */ static void Shuffle(T *apArray, exageostat::dataunits::Locations &aLocations, int aSize); @@ -62,6 +65,7 @@ namespace exageostat::prediction { * @param[in, out] aLocations Locations to be shuffled. * @param[out] aSize Size of data. * @return void + * */ static void Shuffle(T *apArray1, T *apArray2, exageostat::dataunits::Locations &aLocations, int aSize); @@ -73,6 +77,7 @@ namespace exageostat::prediction { * @param[in, out] aLocations Locations to be shuffled. * @param[out] aSize Size of data. * @return void + * */ static void Shuffle(T *apArray1, T *apArray2, T *apArray3, exageostat::dataunits::Locations &aLocations, int aSize); @@ -83,6 +88,7 @@ namespace exageostat::prediction { * @param aCount[in] Number of elements in the input array. * @param aDimension[in] Dimension of the input data. * @return void + * */ static void SortArray(uint32_t *aData, int aCount); @@ -93,6 +99,7 @@ namespace exageostat::prediction { * @param[in,out] aLocations Reference to the Locations object containing X and Y coordinates (input/output). * @param[in,out] apZ Pointer to the array containing observation values (input/output). * @return 0 if the sorting is successful. + * */ static int SortInplace(int aN, exageostat::dataunits::Locations &aLocations, T *apZ); diff --git a/inst/include/results/Results.hpp b/inst/include/results/Results.hpp index 2b227d07..4e21786b 100644 --- a/inst/include/results/Results.hpp +++ b/inst/include/results/Results.hpp @@ -26,227 +26,265 @@ namespace exageostat::results { /** * @brief Get a pointer to the singleton instance of the Results class. * @return A pointer to the instance of the Results class. + * */ static Results *GetInstance(); /** * @brief Set the flag indicating whether the results are synthetic or not. * @param[in] aIsSynthetic True if the results are synthetic, false otherwise. + * */ void SetIsSynthetic(bool aIsSynthetic); /** * @brief Set the number of generated locations. * @param[in] aNumLocations The number of generated locations. + * */ void SetGeneratedLocationsNumber(int aNumLocations); /** * @brief Set the flag indicating whether the logger is active or not. * @param[in] aIsLogger True if the logger is active, false otherwise. + * */ void SetIsLogger(bool aIsLogger); /** * @brief Set the path for the logger. * @param[in] aLoggerPath The path for the logger. + * */ void SetLoggerPath(const std::string &aLoggerPath); /** * @brief Set the Total Data Generation execution time. * @param[in] aTime The execution time. + * */ void SetTotalDataGenerationExecutionTime(double aTime); /** * @brief Set the Data Generation floating-point operations (FLOPs). * @param[in] aFlops The number of FLOPs. + * */ void SetTotalDataGenerationFlops(double aFlops); /** * @brief Set the log-likelihood value. * @param[in] aLogLikValue The log-likelihood value. + * */ void SetLogLikValue(double aLogLikValue); /** * @brief Set the number of maximum likelihood estimation (MLE) iterations. * @param[in] aIterationsNumber The number of MLE iterations. + * */ void SetMLEIterations(int aIterationsNumber); /** * @brief Set the vector of maximum theta values. * @param[in] aMaximumTheta The vector of maximum theta values. + * */ void SetMaximumTheta(const std::vector &aMaximumTheta); /** * @brief Set the total modeling execution time. * @param[in] aTime The total execution time for data modeling. + * */ void SetTotalModelingExecutionTime(double aTime); /** * @brief Get the total modeling execution time. * @return The total execution time for data modeling. + * */ [[nodiscard]] double GetTotalModelingExecutionTime() const; /** * @brief Get the MLOE. * @return The MLOE. + * */ [[nodiscard]] double GetMLOE() const; /** * @brief Get the MSPEError. * @return The MSPEError. + * */ [[nodiscard]] double GetMSPEError() const; /** * @brief Get the IDW error. * @return The the IDW error vector. + * */ [[nodiscard]] std::vector GetIDWError() const; /** * @brief Get the MMOM. * @return The MMOM. + * */ [[nodiscard]] double GetMMOM() const; /** * @brief Get the Fisher matrix elements. * @return the Fisher matrix. + * */ [[nodiscard]] std::vector GetFisherMatrix() const; /** * @brief Get the Predicted Missed Z matrix elements. * @return the Z Predicted matrix. + * */ [[nodiscard]] std::vector GetPredictedMissedValues() const; /** * @brief Set the total modeling FLOPs. * @param[in] aTime The total number of FLOPs for data modeling. + * */ void SetTotalModelingFlops(double aTime); /** * @brief Get the total modeling FLOPs. * @return The total number of FLOPs for data modeling. + * */ [[nodiscard]] double GetTotalModelingFlops() const; /** * @brief Get the average modeling execution time. * @return The average execution time for data modeling. + * */ [[nodiscard]] double GetAverageModelingExecutionTime() const; /** * @brief Get the average modeling FLOPs. * @return The average number of FLOPs for data modeling. + * */ [[nodiscard]] double GetAverageModelingFlops() const; /** * @brief Set the value of ZMiss. * @param[in] aZMiss The value of ZMiss. + * */ void SetZMiss(int aZMiss); /** * @brief Set the value of MSPEError. * @param[in] aMSPEError The value of MSPEError. + * */ void SetMSPEError(double aMSPEError); /** * @brief Set the MSPE execution time. * @param[in] aTime The execution time. + * */ void SetMSPEExecutionTime(double aTime); /** * @brief Set the MSPE number of floating-point operations (FLOPs). * @param[in] aFlops The number of FLOPs. + * */ void SetMSPEFlops(double aFlops); /** * @brief Set the vector of IDW errors. * @param[in] aIDWError The vector of IDW errors. + * */ void SetIDWError(const std::vector &aIDWError); /** * @brief Set the value of MLOE. * @param[in] aMLOE The value of MLOE. + * */ void SetMLOE(double aMLOE); /** * @brief Set the value of MMOM. * @param[in] aMMOM The value of MMOM. + * */ void SetMMOM(double aMMOM); /** * @brief Set the MLOE-MMOM execution time. * @param[in] aTime The execution time. + * */ void SetExecutionTimeMLOEMMOM(double aTime); /** * @brief Set the MLOE-MMOM matrix generation time. * @param[in] aTime The execution time. + * */ void SetMatrixGenerationTimeMLOEMMOM(double aTime); /** * @brief Set the MLOE-MMOM cholesky factorization time. * @param[in] aTime The execution time. + * */ void SetFactoTimeMLOEMMOM(double aTime); /** * @brief Set the MLOE-MMOM loop time. * @param[in] aTime The execution time. + * */ void SetLoopTimeMLOEMMOM(double aTime); /** * @brief Set the MLOE-MMOM number of floating-point operations (FLOPs). * @param[in] aFlops The number of FLOPs. + * */ void SetFlopsMLOEMMOM(double aFlops); /** * @brief Set The total execution time of the fisher tile computation. * @param[in] aTime The total execution time for fisher tile computation. + * */ - void SetTotalFisherTime (double aTime); + void SetTotalFisherTime(double aTime); /** * @brief Set the elements of the fisher matrix. * @param aFisherMatrix Elements of the fisher matrix. + * */ void SetFisherMatrix(std::vector aFisherMatrix); /** * @brief Set the elements of the Z missed matrix. * @param aPredictedValues Elements of the Predicted Z missed matrix. + * */ void SetPredictedMissedValues(std::vector aPredictedValues); /** * @brief Print the end summary of the results. + * */ void PrintEndSummary(); diff --git a/inst/include/runtime/RuntimeFunctions.hpp b/inst/include/runtime/RuntimeFunctions.hpp index db22cfb7..831cf769 100644 --- a/inst/include/runtime/RuntimeFunctions.hpp +++ b/inst/include/runtime/RuntimeFunctions.hpp @@ -45,10 +45,10 @@ namespace exageostat::runtime { * */ static void - CovarianceMatrix(dataunits::DescriptorData &aDescriptorData, void *apDescriptor, const int &aTriangularPart, - dataunits::Locations *apLocation1, dataunits::Locations *apLocation2, - dataunits::Locations *apLocation3, T *apLocalTheta, const int &aDistanceMetric, - const kernels::Kernel *apKernel); + CovarianceMatrix(dataunits::DescriptorData &aDescriptorData, void *apDescriptor, const int &aTriangularPart, + dataunits::Locations *apLocation1, dataunits::Locations *apLocation2, + dataunits::Locations *apLocation3, T *apLocalTheta, const int &aDistanceMetric, + const kernels::Kernel *apKernel); /** * @brief Perform an asynchronous computation of MLE, MLOE, and MMOM for a tile. @@ -93,7 +93,7 @@ namespace exageostat::runtime { * */ static void - CopyDescriptorZ(dataunits::DescriptorData &aDescriptorData, void *apDescriptor, T *apDoubleVector); + CopyDescriptorZ(dataunits::DescriptorData &aDescriptorData, void *apDescriptor, T *apDoubleVector); /** * @brief Converts a Gaussian descriptor to a non-tiled descriptor. @@ -104,7 +104,7 @@ namespace exageostat::runtime { * */ static void - ExaGeoStatGaussianToNonTileAsync(dataunits::DescriptorData &aDescriptorData, void *apDesc, T *apTheta); + ExaGeoStatGaussianToNonTileAsync(dataunits::DescriptorData &aDescriptorData, void *apDesc, T *apTheta); /** * @brief copy Chameleon descriptor to vector float*. diff --git a/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp b/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp index 1401211c..52bd2dff 100644 --- a/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp @@ -52,7 +52,7 @@ namespace exageostat::runtime { * @return void * */ - void InsertTask(const common::Computation& aComputation, void *apDescA, void *apDescDet, + void InsertTask(const common::Computation &aComputation, void *apDescA, void *apDescDet, std::unique_ptr &aStarPuHelpers); private: @@ -73,7 +73,7 @@ namespace exageostat::runtime { * @return T The calculated determinant of the matrix. * */ - static T core_dmdet(const T *apDescriptor,const int &aSize); + static T core_dmdet(const T *apDescriptor, const int &aSize); /// starpu_codelet struct static struct starpu_codelet cl_dmdet; diff --git a/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp b/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp index 2cc0ecc1..af04553b 100644 --- a/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp @@ -72,7 +72,7 @@ namespace exageostat::runtime { * @return The calculated trace of the matrix. * */ - static double core_dtrace(const T *pDescriptor,const int &aSize, T *pTrace); + static double core_dtrace(const T *pDescriptor, const int &aSize, T *pTrace); /// starpu_codelet struct static struct starpu_codelet cl_dtrace; diff --git a/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp b/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp index 86cc001f..c2605f6a 100644 --- a/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp @@ -88,7 +88,8 @@ namespace exageostat::runtime { * @return T The calculated root of the function. * */ - static double newton_raphson(T apDescriptorZ, T aTransLocation, T aTransScale, T aTransShape, T aTransKurtosis, T aEpsilon); + static double + newton_raphson(T apDescriptorZ, T aTransLocation, T aTransScale, T aTransShape, T aTransKurtosis, T aEpsilon); /** * @brief Calculates the Non-Gaussian transformation of a value. @@ -101,7 +102,8 @@ namespace exageostat::runtime { * @return T The transformed value. * */ - static double tukeyGHTransfor(T aOriginalValue, T aCurrentValue, T aTransLocation, T aTransScale, T aTransShape, T aTransKurtosis); + static double tukeyGHTransfor(T aOriginalValue, T aCurrentValue, T aTransLocation, T aTransScale, T aTransShape, + T aTransKurtosis); /** * @brief Calculates the derivative of the Non-Gaussian transformation. diff --git a/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp b/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp index a5fd9cc0..fefa6102 100644 --- a/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp +++ b/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp @@ -63,7 +63,7 @@ namespace exageostat::runtime { * @copydoc StarPuHelpers::ExaGeoStatDataGetAddr() * */ - void *ExaGeoStatDataGetAddr(void *apDescriptor, const int& aDescRow, const int& aDescCol) override; + void *ExaGeoStatDataGetAddr(void *apDescriptor, const int &aDescRow, const int &aDescCol) override; /** * @brief Get the number of tile rows of the sub-matrix diff --git a/inst/include/utilities/EnumStringParser.hpp b/inst/include/utilities/EnumStringParser.hpp index cf05b89f..6d3ed072 100644 --- a/inst/include/utilities/EnumStringParser.hpp +++ b/inst/include/utilities/EnumStringParser.hpp @@ -19,26 +19,24 @@ #include #include -using namespace exageostat::common; - /** * @brief Convert a string representation of computation mode to its corresponding enum value. * @param[in] aComputation String representation of computation mode. * @return Computation enum value. */ -inline Computation GetInputComputation(std::string aComputation) { +inline exageostat::common::Computation GetInputComputation(std::string aComputation) { std::transform(aComputation.begin(), aComputation.end(), aComputation.begin(), ::tolower); if (aComputation == "exact" || aComputation == "dense") { - return EXACT_DENSE; + return exageostat::common::EXACT_DENSE; } else if (aComputation == "dst" || aComputation == "diag_approx") { - return DIAGONAL_APPROX; + return exageostat::common::DIAGONAL_APPROX; } else if (aComputation == "tlr" || aComputation == "tile_low_rank") { - return TILE_LOW_RANK; + return exageostat::common::TILE_LOW_RANK; } else { const std::string msg = "Error in Initialization : Unknown computation Value" + std::string(aComputation); - throw API_EXCEPTION(msg, INVALID_ARGUMENT_ERROR); + throw API_EXCEPTION(msg, INVALID_ARGUMENT_ERROR); } } @@ -47,16 +45,16 @@ inline Computation GetInputComputation(std::string aComputation) { * @param[in] aDimension Dimension as a string. * @return Dimension as an enum. */ -inline Dimension GetInputDimension(std::string aDimension) { +inline exageostat::common::Dimension GetInputDimension(std::string aDimension) { std::transform(aDimension.begin(), aDimension.end(), aDimension.begin(), ::tolower); if (aDimension == "2d") { - return Dimension2D; + return exageostat::common::Dimension2D; } else if (aDimension == "3d") { - return Dimension3D; + return exageostat::common::Dimension3D; } else if (aDimension == "st") { - return DimensionST; + return exageostat::common::DimensionST; } else { const std::string msg = "Error in Initialization : Unknown computation Value" + std::string(aDimension); throw API_EXCEPTION(msg, INVALID_ARGUMENT_ERROR); diff --git a/inst/include/utilities/ErrorHandler.hpp b/inst/include/utilities/ErrorHandler.hpp index f9cb1c84..21e51b15 100644 --- a/inst/include/utilities/ErrorHandler.hpp +++ b/inst/include/utilities/ErrorHandler.hpp @@ -22,7 +22,7 @@ /** * @brief EXAGEOSTAT API Exceptions Macro to use for Errors and Warnings. */ - #define API_EXCEPTION(MESSAGE, ERROR_TYPE) \ +#define API_EXCEPTION(MESSAGE, ERROR_TYPE) \ APIException(MESSAGE, ERROR_TYPE) #ifdef USE_CUDA @@ -47,7 +47,7 @@ enum ErrorType : int { * @class APIException * @brief Custom exception class for handling API errors and warnings. */ -class APIException : public std::exception{ +class APIException : public std::exception { public: @@ -58,10 +58,12 @@ class APIException : public std::exception{ */ APIException(const std::string &aMessage, const ErrorType &aErrorCode) { - if (aErrorCode != WARNING) { - APIException::ThrowError(aMessage, aErrorCode); - } else { - APIException::ThrowWarning(aMessage); + if (aErrorCode == RUNTIME_ERROR) { + throw std::runtime_error(aMessage); + } else if (aErrorCode == INVALID_ARGUMENT_ERROR) { + throw std::invalid_argument(aMessage); + } else if (aErrorCode == RANGE_ERROR) { + throw std::range_error(aMessage); } } @@ -81,49 +83,13 @@ class APIException : public std::exception{ { if (aCode != cudaSuccess) { -#ifdef USING_R - std::string s="GPU Assert: "+std::string(cudaGetErrorString(aCode)); -// Rcpp::stop(s); -#else char s[200]; sprintf((char*)s,"GPU Assert: %s %s %d\n", cudaGetErrorString(aCode), aFile, aLine); throw std::invalid_argument(s); -#endif } } #endif - -private: - - /** - * @brief Helper function to throw error based on error type. - * @param[in] aString The error message. - * @param[in] aErrorCode The error type. - */ - static void ThrowError(const std::string &aString, const ErrorType &aErrorCode) { -#ifdef USING_R -// Rcpp::stop(aString); -#else - if (aErrorCode == RUNTIME_ERROR) { - throw std::runtime_error(aString); - } else if (aErrorCode == INVALID_ARGUMENT_ERROR) { - throw std::invalid_argument(aString); - } else if (aErrorCode == RANGE_ERROR) { - throw std::range_error(aString); - } -#endif - } - - /** - * @brief Helper function to throw warning. - * @param[in] aString The warning message. - */ - static void ThrowWarning(const std::string &aString) { -#ifdef USING_R -// Rcpp::warning(aString); -#endif - } }; #endif //EXAGEOSTATCPP_ERRORHANDLER_HPP \ No newline at end of file diff --git a/man/Data.Rd b/man/Data.Rd index dd9552d0..3e55914a 100644 --- a/man/Data.Rd +++ b/man/Data.Rd @@ -22,9 +22,9 @@ for location and descriptor manipulation. It is initialized with the size and di \code{\link{ExaGeoStatData}} Creates a new instance of the \code{ExaGeoStatData} class. \code{ExaGeoStatData(size, dimension)} \describe{ - \item{\code{size}}{An integer representing the size of the data.} - \item{\code{dimension}}{A string representing the dimensions of the data.} -} + \item{\code{size}}{An integer representing the size of the locations data.} + \item{\code{dimension}}{A string representing the dimensions of the data. - available dimension ("2D", "3D", "ST")} + } } \value{ @@ -33,7 +33,7 @@ An object of class \code{ExaGeoStatData} representing a data component with the \examples{ problem_size <- 4 -dimension = "2D" +dimension = "3D" empty_data <- new(Data, problem_size, dimension) } diff --git a/man/Hardware.Rd b/man/Hardware.Rd index d59f956b..142cb2ab 100644 --- a/man/Hardware.Rd +++ b/man/Hardware.Rd @@ -22,7 +22,7 @@ It is initialized with computation mode, and two integers representing number of \code{\link{ExaGeoStatHardware}} Creates a new instance of the \code{ExaGeoStatHardware} class. \code{ExaGeoStatHardware(computation, num_of_cpus, num_of_gpus)} \describe{ - \item{\code{computation}}{A string representing the computation mode.} + \item{\code{computation}}{A string specifying the computation method, either "exact" or "dst" or "tlr".} \item{\code{num_of_cpus}}{An integer representing number of CPU cores.} \item{\code{num_of_gpus}}{An integer representing number of GPU cores.} } diff --git a/man/fisher.Rd b/man/fisher.Rd index 69ddb2de..9d038c3a 100644 --- a/man/fisher.Rd +++ b/man/fisher.Rd @@ -13,8 +13,9 @@ \alias{fisher} \title{Compute the Fisher information matrix for a given data and theta vector} -\usage{fisher(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", z_miss, data, -matrix = R_NilValue, x = R_NilValue, y = R_NilValue, z = R_NilValue)} +\usage{ +fisher(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", train_data, test_data) +} \arguments{ \item{kernel}{A string specifying the kernel to use - available kernels @@ -62,12 +63,8 @@ matrix = R_NilValue, x = R_NilValue, y = R_NilValue, z = R_NilValue)} \item{dts}{A numeric value representing the time step size.} \item{lts}{A numeric value representing the length step size. Default is 0.} \item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D".} -\item{z_miss}{A numeric value representing the missing value indicator.} -\item{data}{A list of data vectors.} -\item{matrix}{A matrix object. Default is `R_NilValue`.} -\item{x}{ A numeric vector. Default is `R_NilValue`.} -\item{y}{ A numeric vector. Default is `R_NilValue`.} -\item{z}{ A numeric vector. Default is `R_NilValue`.} +\item{train_data}{ A numeric vector contains the locations and z measurements for training} +\item{test_data}{ A numeric vector contains the locations for testing.} } \value{A vector containing the Fisher information matrix elements.} @@ -76,18 +73,22 @@ matrix = R_NilValue, x = R_NilValue, y = R_NilValue, z = R_NilValue)} using a specified kernel and distance metric. It also allows for the inclusion of missing values and the specification of data dimensions.} \examples{ -ncores <- 2 +dimension = "2D" +ncores <- 1 ngpus <- 0 +dts <- 2 +kernel <- "univariate_matern_stationary" +estimated_theta <- c(1,0.1,0.5) computation <- "exact" + hardware <- new(Hardware, computation, ncores, ngpus) -problem_size <- 4 -dimension = "2D" -dts <- 2 -z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) -x_locations <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) -y_locations <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) -empty_data <- new(Data, problem_size, dimension) +z_value <- c(-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) +locations_x <- c(0.092042420080872822, 0.193041886015106440, 0.330556191348134576, 0.181612878614480805) +locations_y <- c(0.928648813611047563, 0.103883421072709245, 0.135790035858701447, 0.434683756771190977) + +test_x <- c(0.347951, 0.62768) +test_y <- c(0.806332, 0.105196) -fisher(kernel = "univariate_matern_stationary" , estimated_theta = c(1,0.1,0.5), dts = 2 , z_miss = 3, data=empty_data, matrix = z_value, x = x_locations, y = y_locations) +fisher_matrix <- fisher(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) } diff --git a/man/get_Z_measurement_vector.Rd b/man/get_Z_measurement_vector.Rd index 44fa85b4..1054de28 100644 --- a/man/get_Z_measurement_vector.Rd +++ b/man/get_Z_measurement_vector.Rd @@ -22,7 +22,7 @@ get_Z_measurement_vector(data,type) } \arguments{ -\item{data}{A list of ExaGeoStatData that contains the locations. +\item{data}{A list of ExaGeoStatData that contains the locations.} \item{type}{A string specifying the type of descriptor value to retrieve (e.g., "Chameleon", "HiCMA").} } @@ -43,7 +43,6 @@ empty_data <- new(Data, problem_size, dimension) dts <- 2 kernel <- "univariate_matern_stationary" initial_theta <- c(1,0.1,0.5) - exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) Z <- get_Z_measurement_vector(data=exageostat_data, type="chameleon") diff --git a/man/get_locationsX.Rd b/man/get_locationsX.Rd index 016c1af9..fe652279 100644 --- a/man/get_locationsX.Rd +++ b/man/get_locationsX.Rd @@ -30,7 +30,7 @@ A numeric vector of X locations. } \examples{ -ncores <- 2 +ncores <- 1 ngpus <- 0 computation <- "exact" hardware <- new(Hardware, computation, ncores, ngpus) @@ -42,7 +42,6 @@ empty_data <- new(Data, problem_size, dimension) dts <- 2 kernel <- "univariate_matern_stationary" initial_theta <- c(1,0.1,0.5) - exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) x <- get_locationsX(data=exageostat_data) diff --git a/man/get_locationsY.Rd b/man/get_locationsY.Rd index 839e4d52..c3da0bd5 100644 --- a/man/get_locationsY.Rd +++ b/man/get_locationsY.Rd @@ -42,7 +42,6 @@ empty_data <- new(Data, problem_size, dimension) dts <- 2 kernel <- "univariate_matern_stationary" initial_theta <- c(1,0.1,0.5) - exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) y <- get_locationsY(data=exageostat_data) diff --git a/man/get_locationsZ.Rd b/man/get_locationsZ.Rd index ee8f418a..daaa407c 100644 --- a/man/get_locationsZ.Rd +++ b/man/get_locationsZ.Rd @@ -42,7 +42,6 @@ empty_data <- new(Data, problem_size, dimension) dts <- 2 kernel <- "univariate_matern_stationary" initial_theta <- c(1,0.1,0.5) - exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) z <- get_locationsZ(data=exageostat_data) diff --git a/man/idw.Rd b/man/idw.Rd index 8a7f0838..bc5bbf54 100644 --- a/man/idw.Rd +++ b/man/idw.Rd @@ -14,7 +14,7 @@ \title{This function performs IDW interpolation for a given dataset and theta vector.} \usage{ -idw(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", z_miss, data, matrix = R_NilValue, x = R_NilValue, y = R_NilValue, z = R_NilValue) +idw(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", train_data, test_data, test_measurements) } \arguments{ @@ -63,16 +63,13 @@ idw(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimens \item{dts}{A numeric value representing the time step size} \item{lts}{A numeric value representing the length step size. Default is 0} \item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D"} - \item{z_miss}{ A numeric value representing the missing value indicator} - \item{data}{ A list of data vectors} - \item{matrix}{ A matrix object. Default is `R_NilValue`} - \item{x}{A numeric vector. Default is `R_NilValue`} - \item{y}{A numeric vector. Default is `R_NilValue`} - \item{z}{A numeric vector. Default is `R_NilValue`} + \item{train_data}{ A numeric vector contains the locations and z measurements for training} + \item{test_data}{ A numeric vector contains the locations for testing.} + \item{test_measurements}{ A numeric vector contains the z measurements for testing.} } \value{ - A vector containing the IDW data. + A vector containing the IDW error. } \description{ @@ -83,16 +80,20 @@ This function performs Inverse Distance Weighting (IDW) interpolation for a give ncores <- 2 ngpus <- 0 computation <- "exact" - hardware <- new(Hardware, computation, ncores, ngpus) problem_size <- 4 dimension = "2D" dts <- 2 +kernel <- "univariate_matern_stationary" +estimated_theta <- c(1,0.1,0.5) + z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) -x_locations <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) -y_locations <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) +test_x <- c(0.347951, 0.62768) +test_y <- c(0.806332, 0.105196) +test_measurements = c(-1.05428, -1.47441) -empty_data <- new(Data, problem_size, dimension) -idw(kernel = "univariate_matern_stationary" , estimated_theta = c(1,0.1,0.5), dts = 2 , z_miss = 3, data=empty_data, matrix = z_value, x = x_locations, y = y_locations) +idw_error = idw(kernel=kernel, estimated_theta=estimated_theta, dts=dts, train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), test_measurements=test_measurements) } diff --git a/man/mloe_mmom.Rd b/man/mloe_mmom.Rd index b805ba2f..e8c7c382 100644 --- a/man/mloe_mmom.Rd +++ b/man/mloe_mmom.Rd @@ -14,7 +14,7 @@ \title{Mean Misspecification of the Mean Square Error (MMOM) and Mean Loss of Efficiency (MLOE) using exact method.} \usage{ -mloe_mmom(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, true_theta=estimated_theta, z_miss=3 ) +mloe_mmom(kernel, distance_matrix="euclidean", estimated_theta, true_theta, dts, lts=0, dimension="2D", train_data, test_data) } \arguments{ @@ -60,15 +60,12 @@ mloe_mmom(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel= )} \item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} \item{estimated_theta}{A list of estimated theta parameters} +\item{true_theta}{A list of truth theta parameters} \item{dts}{A numeric value representing the time step size} \item{lts}{A numeric value representing the length step size. Default is 0} \item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D"} -\item{z_miss}{ A numeric value representing the missing value indicator} -\item{data}{ A list of data vectors} -\item{matrix}{ A matrix object. Default is `R_NilValue`} -\item{x}{A numeric vector. Default is `R_NilValue`} -\item{y}{A numeric vector. Default is `R_NilValue`} -\item{z}{A numeric vector. Default is `R_NilValue`} +\item{train_data}{ A numeric vector contains the locations and z measurements for training} +\item{test_data}{ A numeric vector contains the locations for testing.} } \value{ @@ -83,16 +80,21 @@ This function calculates Mean Misspecification of the Mean Square Error (MMOM) a ncores <- 2 ngpus <- 0 computation <- "exact" - hardware <- new(Hardware, computation, ncores, ngpus) problem_size <- 4 dimension = "2D" dts <- 2 -z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) -x_locations <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) -y_locations <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) +kernel <- "univariate_matern_stationary" +estimated_theta <- c(1,0.1,0.5) +true_theta <- c(1.1,0.2,0.5) + +z_value <- c(-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) +locations_x <- c(0.092042420080872822, 0.193041886015106440, 0.330556191348134576, 0.181612878614480805) +locations_y <- c(0.928648813611047563, 0.103883421072709245, 0.135790035858701447, 0.434683756771190977) + +test_x <- c(0.347951, 0.62768) +test_y <- c(0.806332, 0.105196) -empty_data <- new(Data, problem_size, dimension) -mloe_mmom(data=empty_data, matrix=z_value, x=x_locations, y=y_locations, kernel="univariate_matern_stationary", dts = 2, estimated_theta=c(1,0.1,0.5), true_theta=c(1,0.1,0.5), z_miss=3 ) +result_mloe_mmom = mloe_mmom(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta, true_theta=true_theta) } diff --git a/man/model_data.Rd b/man/model_data.Rd index 4346d0bd..3d5bfbe7 100644 --- a/man/model_data.Rd +++ b/man/model_data.Rd @@ -14,11 +14,11 @@ \title{This function models data based on the provided computation method, kernel, distance matrix, and other parameters.} \usage{ -model_data(computation = "exact", kernel, distance_matrix = "euclidean", lb, ub, tol = 4, mle_itr, dts, lts = 0, dimension = "2D", data, matrix = R_NilValue, x = R_NilValue, y = R_NilValue, z = R_NilValue) +model_data(computation = "exact", kernel, distance_matrix = "euclidean", lb, ub, tol = 4, mle_itr, dts, lts = 0, dimension = "2D", band = 0, max_rank = 500, data = NULL, matrix = NULL, x = NULL, y = NULL, z = NULL) } \arguments{ -\item{computation}{A string specifying the computation method, either "exact" or another method supported by the package. Default is "exact".} +\item{computation}{A string specifying the computation method, either "exact" or "dst" or "tlr". Default is "exact".} \item{kernel}{A string specifying the kernel to use - available kernels ( "BivariateMaternFlexible", "BivariateMaternParsimonious", @@ -67,7 +67,9 @@ model_data(computation = "exact", kernel, distance_matrix = "euclidean", lb, ub, \item{dts}{A numeric value representing the time step size.} \item{lts}{A numeric value representing the length step size. Default is 0.} \item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D".} -\item{data}{A list of data vectors.} +\item{band}{A numeric value Bandwidth for band matrices, applicable in certain computational kernels.. Default is 0.} +\item{max_rank}{A numeric value specifying the Maximum rank for low-rank approximations.. Default is 500.} +\item{data}{A list of data vectors. Default is `R_NilValue`.} \item{matrix}{A matrix object. Default is `R_NilValue`.} \item{x}{A numeric vector. Default is `R_NilValue`.} \item{y}{A numeric vector. Default is `R_NilValue`.} @@ -90,7 +92,6 @@ hardware <- new(Hardware, computation, ncores, ngpus) dimension = "2D" problem_size <- 4 - empty_data <- new(Data, problem_size, dimension) dts <- 2 @@ -102,6 +103,5 @@ z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967 locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) -theta <- model_data(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, - dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10, computation=computation) +theta <- model_data(kernel=kernel, lb=lower_bound, ub=upper_bound, mle_itr=10, dts=dts, matrix=z_value, x=locations_x, y=locations_y) } diff --git a/man/predict_data.Rd b/man/predict_data.Rd index 233c06c9..3bf54c4c 100644 --- a/man/predict_data.Rd +++ b/man/predict_data.Rd @@ -14,7 +14,7 @@ \title{This function predicts data based on the provided kernel, distance matrix, estimated theta, and other parameters.} \usage{ -predict_data(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", z_miss, data, matrix = R_NilValue, x = R_NilValue, y = R_NilValue, z = R_NilValu) +predict_data(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", train_data, test_data) } \arguments{ @@ -63,38 +63,33 @@ predict_data(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = \item{dts}{A numeric value representing the time step size} \item{lts}{A numeric value representing the length step size. Default is 0} \item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D"} - \item{z_miss}{ A numeric value representing the missing value indicator} - \item{data}{ A list of data vectors} - \item{matrix}{ A matrix object. Default is `R_NilValue`} - \item{x}{A numeric vector. Default is `R_NilValue`} - \item{y}{A numeric vector. Default is `R_NilValue`} - \item{z}{A numeric vector. Default is `R_NilValue`} + \item{train_data}{ A numeric vector contains the locations and z measurements for training} + \item{test_data}{ A numeric vector contains the locations for testing.} } \value{ - N/A - } + A vector of predicted z values +} \description{ This function predicts data based on the provided kernel, distance matrix, estimated theta, and other parameters. } \examples{ -dimension = "2D" ncores <- 2 ngpus <- 0 problem_size <- 4 dts <- 2 -lts <- 0 computation <- "exact" +hardware <- new(Hardware, computation, ncores, ngpus) kernel <- "univariate_matern_stationary" estimated_theta <- c(1,0.1,0.5) -hardware <- new(Hardware, computation, ncores, ngpus) -empty_data <- new(Data, problem_size, dimension) z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) +test_x <- c(0.347951, 0.62768) +test_y <- c(0.806332, 0.105196) -predict_data(data=empty_data, matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, estimated_theta=estimated_theta, z_miss=3) +predict_data(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) } diff --git a/man/simulate_data.Rd b/man/simulate_data.Rd index 4ab8e201..25bf8553 100644 --- a/man/simulate_data.Rd +++ b/man/simulate_data.Rd @@ -61,9 +61,9 @@ simulate_data(kernel, initial_theta, distance_matrix = "euclidean", problem_size \item{initial_theta}{A list of initial theta parameters.} \item{distance_matrix}{A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} \item{problem_size}{A numeric value representing the size of the problem to simulate.} -\item{seed A numeric}{value specifying the seed for random number generation. Default is 0.} -\item{dts A numeric}{value representing the time step size.} -\item{lts A numeric}{value representing the length step size. Default is 0.} +\item{seed}{ A numeric value specifying the seed for random number generation. Default is 0.} +\item{dts}{ A numeric value representing the time step size.} +\item{lts}{ A numeric value representing the length step size. Default is 0.} \item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D".} \item{log_path}{A string specifying the path for logging. Default is "".} \item{data_path}{A string specifying the path for data storage. Default is "".} @@ -91,9 +91,6 @@ empty_data <- new(Data, problem_size, dimension) dts <- 2 kernel <- "univariate_matern_stationary" -z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) -locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) -locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) initial_theta <- c(1,0.1,0.5) exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) diff --git a/scripts/Benchmarking.sh b/scripts/Benchmarking.sh index a21c9ae3..e14bafd8 100644 --- a/scripts/Benchmarking.sh +++ b/scripts/Benchmarking.sh @@ -3,13 +3,13 @@ # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). -# @file config.sh +# @file Benchmarking.sh # @version 1.1.0 # @author Mahmoud ElKarargy # @date 2023-10-10 # Set the output file name -output_file="VULTURE_40CORE_EXACT.csv" +output_file="40CORE_EXACT.csv" # Create or truncate the output file echo "N,computation,kernel,ncores,ngpus,dts,Zmiss,maximum_likelihood,maximum_theta,mspe_value,time_generation,time_modeling,time_prediction,time_modeling_per_iter,Gflops_data_generation,Gflops_data_modeling,Gflops_data_modeling_iter,Gflops_data_mspe" > "$output_file" @@ -60,8 +60,8 @@ for N in "${desired_N[@]}"; do echo "Iteration number $iteration done." # Append the values to the output file echo '**Results**' - echo "$N,$COMPUTATION,$KERNEL,$CORES,$GPUS,$DTS,$(($N/10)),$logli_result,$maximum_theta,$mspe_value,$time_generation,$time_modeling,$time_prediction,$time_modeling_iteration,$flops_generation,$flops_modeling,$flops_modeling_iteration,$flops_mspe" + echo "$N,$COMPUTATION,$KERNEL,$CORES,$GPUS,$DTS,$((N/10)),$logli_result,$maximum_theta,$mspe_value,$time_generation,$time_modeling,$time_prediction,$time_modeling_iteration,$flops_generation,$flops_modeling,$flops_modeling_iteration,$flops_mspe" echo '' - echo "$N,$COMPUTATION,$KERNEL,$CORES,$GPUS,$DTS,$(($N/10)),$logli_result,$maximum_theta,$mspe_value,$time_generation,$time_modeling,$time_prediction,$time_modeling_iteration,$flops_generation,$flops_modeling,$flops_modeling_iteration,$flops_mspe" >> "$output_file" + echo "$N,$COMPUTATION,$KERNEL,$CORES,$GPUS,$DTS,$((N/10)),$logli_result,$maximum_theta,$mspe_value,$time_generation,$time_modeling,$time_prediction,$time_modeling_iteration,$flops_generation,$flops_modeling,$flops_modeling_iteration,$flops_mspe" >> "$output_file" done done diff --git a/src/Rcpp-adapters/FunctionsAdapter.cpp b/src/Rcpp-adapters/FunctionsAdapter.cpp index 1161a87d..3cebb7c3 100644 --- a/src/Rcpp-adapters/FunctionsAdapter.cpp +++ b/src/Rcpp-adapters/FunctionsAdapter.cpp @@ -68,11 +68,9 @@ namespace exageostat::adapters { if (aType == "chameleon" || aType == "Chameleon") { descriptorType = CHAMELEON_DESCRIPTOR; - pDescriptor = apData->GetDescriptorData()->GetDescriptor(descriptorType, DESCRIPTOR_Z).chameleon_desc; } else if (aType == "hicma" || aType == "Hicma" || aType == "HICMA") { #ifdef USE_HICMA descriptorType = HICMA_DESCRIPTOR; - pDescriptor = apData->GetDescriptorData()->GetDescriptor(descriptorType, DESCRIPTOR_Z).hicma_desc; #else throw runtime_error("Please enable HiCMA to use HiCMA descriptors."); #endif @@ -81,7 +79,7 @@ namespace exageostat::adapters { } // Obtain the pointer to the array of doubles - double *data = apData->GetDescriptorData()->GetDescriptorMatrix(descriptorType, pDescriptor); + double *data = apData->GetDescriptorData()->GetDescriptorMatrix(descriptorType, DESCRIPTOR_Z); int length = apData->GetLocations()->GetSize(); // Create an empty NumericVector of the appropriate length NumericVector vec(length); @@ -130,10 +128,9 @@ namespace exageostat::adapters { R_ExaGeoStatModelData(const string &aComputation, const string &aKernelName, const string &aDistanceMatrix, const vector &aLowerBound, const vector &aUpperBound, const int &aTolerance, const int &aMleIterations, const int &aDenseTileSize, const int &aLowTileSize, - const string &aDimension, const int &aBand, const int &aMaxRank, - SEXP apData, Nullable aMeasurementsVector, - Nullable aLocationsX, Nullable aLocationsY, - Nullable aLocationsZ) { + const string &aDimension, const int &aBand, const int &aMaxRank, SEXP apData, + Nullable aMeasurementsVector, Nullable aLocationsX, + Nullable aLocationsY, Nullable aLocationsZ) { Configurations configurations; bool is_initialized = ((SEXP) apData == R_NilValue); @@ -149,9 +146,8 @@ namespace exageostat::adapters { data = make_unique>(z_values.size(), configurations.GetDimension()); } double *pMeasurementsVectorPtr = GetDataFromArguments(aMeasurementsVector, aLocationsX, aLocationsY, - aLocationsZ, data, configurations, - aKernelName, aDistanceMatrix, aDenseTileSize, - aLowTileSize, aDimension, + aLocationsZ, data, configurations, aKernelName, + aDistanceMatrix, aDenseTileSize, aLowTileSize, aDimension, Configurations::CheckComputationValue(aComputation)); configurations.SetLowerBounds(aLowerBound); @@ -168,95 +164,32 @@ namespace exageostat::adapters { } vector R_ExaGeoStatPredictData(const string &aKernelName, const string &aDistanceMatrix, - const vector &aEstimatedTheta, - const int &aDenseTileSize, const int &aLowTileSize, - const string &aDimension, - vector> &aTrainData, - vector> &aTestData) { + const vector &aEstimatedTheta, const int &aDenseTileSize, + const int &aLowTileSize, const string &aDimension, + vector > &aTrainData, vector > &aTestData) { Configurations configurations; - configurations.SetComputation(EXACT_DENSE); - - auto data = make_unique>(aTrainData[0].size(), configurations.GetDimension()); - auto *train_locations = new dataunits::Locations(aTrainData[0].size(), configurations.GetDimension()); - auto *test_locations = new dataunits::Locations(aTestData[0].size(), configurations.GetDimension()); - - auto* z_values = new double[aTrainData.back().size()]; - for(int i = 0; i< aTrainData[0].size(); i++){ - train_locations->SetLocationX(*aTrainData[0].data(), aTrainData[0].size()); - train_locations->SetLocationY(*aTrainData[1].data(), aTrainData[1].size()); - if(aTrainData.size() > 3) { - train_locations->SetLocationZ(*aTrainData[2].data(), aTrainData[2].size()); - } - } - memcpy(z_values, aTrainData.back().data(), aTrainData.back().size() * sizeof(double)); - for(int i = 0; i< aTestData[0].size(); i++){ - test_locations->SetLocationX(*aTestData[0].data(), aTestData[0].size()); - test_locations->SetLocationY(*aTestData[1].data(), aTestData[1].size()); - } - - // Set common configurations. - configurations.CheckKernelValue(aKernelName); - configurations.ParseDistanceMetric(aDistanceMatrix); - configurations.SetDenseTileSize(aDenseTileSize); - configurations.SetLowTileSize(aLowTileSize); - configurations.SetDimension(Configurations::CheckDimensionValue(aDimension)); - configurations.SetProblemSize(data->GetLocations()->GetSize()); - configurations.SetEstimatedTheta(aEstimatedTheta); configurations.SetIsMSPE(TRUE); - - ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_values, train_locations, test_locations); - // Take back ownership, to avoid deleting apData when the unique_ptr goes out of scope. - auto temp_data = data.release(); - delete[] z_values; - + configurations.SetEstimatedTheta(aEstimatedTheta); + vector empty_vector; + PredictionSetupHelper(configurations, aKernelName, aDistanceMatrix, aDenseTileSize, aLowTileSize, aDimension, + aTrainData, aTestData, aEstimatedTheta, empty_vector); return Results::GetInstance()->GetPredictedMissedValues(); } vector R_ExaGeoStatMLOE_MMOM(const string &aKernelName, const string &aDistanceMatrix, const vector &aEstimatedTheta, const vector &aTrueTheta, const int &aDenseTileSize, const int &aLowTileSize, const string &aDimension, - vector> &aTrainData, - vector> &aTestData) { + vector > &aTrainData, vector > &aTestData) { Configurations configurations; - configurations.SetComputation(EXACT_DENSE); - - auto data = make_unique>(aTrainData[0].size(), configurations.GetDimension()); - auto *train_locations = new dataunits::Locations(aTrainData[0].size(), configurations.GetDimension()); - auto *test_locations = new dataunits::Locations(aTestData[0].size(), configurations.GetDimension()); - - auto* z_values = new double[aTrainData.back().size()]; - for(int i = 0; i< aTrainData[0].size(); i++){ - train_locations->SetLocationX(*aTrainData[0].data(), aTrainData[0].size()); - train_locations->SetLocationY(*aTrainData[1].data(), aTrainData[1].size()); - if(aTrainData.size() > 3) { - train_locations->SetLocationZ(*aTrainData[2].data(), aTrainData[2].size()); - } - } - data->SetLocations(*train_locations); - memcpy(z_values, aTrainData.back().data(), aTrainData.back().size() * sizeof(double)); - for(int i = 0; i< aTestData[0].size(); i++){ - test_locations->SetLocationX(*aTestData[0].data(), aTestData[0].size()); - test_locations->SetLocationY(*aTestData[1].data(), aTestData[1].size()); - } - - // Set common configurations. - configurations.CheckKernelValue(aKernelName); - configurations.ParseDistanceMetric(aDistanceMatrix); - configurations.SetDenseTileSize(aDenseTileSize); - configurations.SetLowTileSize(aLowTileSize); - configurations.SetDimension(Configurations::CheckDimensionValue(aDimension)); - configurations.SetProblemSize(data->GetLocations()->GetSize()); configurations.SetEstimatedTheta(aEstimatedTheta); configurations.SetInitialTheta(aTrueTheta); configurations.SetIsMLOEMMOM(TRUE); - ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_values, train_locations, test_locations); - - // Take back ownership, to avoid deleting apData when the unique_ptr goes out of scope. - auto temp_data = data.release(); - delete[] z_values; + vector empty_vector; + PredictionSetupHelper(configurations, aKernelName, aDistanceMatrix, aDenseTileSize, aLowTileSize, aDimension, + aTrainData, aTestData, aEstimatedTheta, empty_vector); vector mloe_mmom_values; mloe_mmom_values.push_back(Results::GetInstance()->GetMLOE()); @@ -264,101 +197,32 @@ namespace exageostat::adapters { return mloe_mmom_values; } - vector R_ExaGeoStatFisher(const string &aKernelName, const string &aDistanceMatrix, const vector &aEstimatedTheta, const int &aDenseTileSize, const int &aLowTileSize, const string &aDimension, - vector> &aTrainData, - vector> &aTestData) { + vector > &aTrainData, vector > &aTestData) { Configurations configurations; - configurations.SetComputation(EXACT_DENSE); - - auto data = make_unique>(aTrainData[0].size(), configurations.GetDimension()); - auto *train_locations = new dataunits::Locations(aTrainData[0].size(), configurations.GetDimension()); - auto *test_locations = new dataunits::Locations(aTestData[0].size(), configurations.GetDimension()); - - auto* z_values = new double[aTrainData.back().size()]; - for(int i = 0; i< aTrainData[0].size(); i++){ - train_locations->SetLocationX(*aTrainData[0].data(), aTrainData[0].size()); - train_locations->SetLocationY(*aTrainData[1].data(), aTrainData[1].size()); - if(aTrainData.size() > 3) { - train_locations->SetLocationZ(*aTrainData[2].data(), aTrainData[2].size()); - } - } - data->SetLocations(*train_locations); - memcpy(z_values, aTrainData.back().data(), aTrainData.back().size() * sizeof(double)); - for(int i = 0; i< aTestData[0].size(); i++){ - test_locations->SetLocationX(*aTestData[0].data(), aTestData[0].size()); - test_locations->SetLocationY(*aTestData[1].data(), aTestData[1].size()); - } - - // Set common configurations. - configurations.CheckKernelValue(aKernelName); - configurations.ParseDistanceMetric(aDistanceMatrix); - configurations.SetProblemSize(data->GetLocations()->GetSize()); - configurations.SetDenseTileSize(aDenseTileSize); - configurations.SetLowTileSize(aLowTileSize); - configurations.SetEstimatedTheta(aEstimatedTheta); configurations.SetIsFisher(TRUE); - ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_values, train_locations, test_locations); - // Take back ownership, to avoid deleting apData when the unique_ptr goes out of scope. - auto temp_data = data.release(); - delete[] z_values; + vector empty_vector; + PredictionSetupHelper(configurations, aKernelName, aDistanceMatrix, aDenseTileSize, aLowTileSize, aDimension, + aTrainData, aTestData, aEstimatedTheta, empty_vector); + return Results::GetInstance()->GetFisherMatrix(); } - vector R_ExaGeoStatIDW(const string &aKernelName, const string &aDistanceMatrix, - const vector &aEstimatedTheta, - const int &aDenseTileSize, const int &aLowTileSize, - const string &aDimension, - vector> &aTrainData, - vector> &aTestData, vector &aTestMeasurementsValues) { + vector + R_ExaGeoStatIDW(const string &aKernelName, const string &aDistanceMatrix, const vector &aEstimatedTheta, + const int &aDenseTileSize, const int &aLowTileSize, const string &aDimension, + vector > &aTrainData, vector > &aTestData, + vector &aTestMeasurementsValues) { Configurations configurations; - configurations.SetComputation(EXACT_DENSE); - - auto data = make_unique>(aTrainData[0].size(), configurations.GetDimension()); - auto *train_locations = new dataunits::Locations(aTrainData[0].size(), configurations.GetDimension()); - auto *test_locations = new dataunits::Locations(aTestData[0].size(), configurations.GetDimension()); - - // Allocate memory for z_values to hold elements from both sources - auto* z_values = new double[aTrainData.back().size() + aTestMeasurementsValues.size()]; - - for(int i = 0; i< aTrainData[0].size(); i++){ - train_locations->SetLocationX(*aTrainData[0].data(), aTrainData[0].size()); - train_locations->SetLocationY(*aTrainData[1].data(), aTrainData[1].size()); - if(aTrainData.size() > 3) { - train_locations->SetLocationZ(*aTrainData[2].data(), aTrainData[2].size()); - } - } - // Copy data from the last element of aTrainData to the beginning of z_values - memcpy(z_values, aTrainData.back().data(), aTrainData.back().size() * sizeof(double)); - // Calculate the starting position for the next part of the data in z_values - auto* destination = z_values + aTrainData.back().size(); - // Copy data from aTestMeasurementsValues to the next part of z_values, after the previously copied data - memcpy(destination, aTestMeasurementsValues.data(), aTestMeasurementsValues.size() * sizeof(double)); - - for(int i = 0; i< aTestData[0].size(); i++){ - test_locations->SetLocationX(*aTestData[0].data(), aTestData[0].size()); - test_locations->SetLocationY(*aTestData[1].data(), aTestData[1].size()); - } - - // Set common configurations. - configurations.CheckKernelValue(aKernelName); - configurations.ParseDistanceMetric(aDistanceMatrix); - configurations.SetDenseTileSize(aDenseTileSize); - configurations.SetLowTileSize(aLowTileSize); - configurations.SetDimension(Configurations::CheckDimensionValue(aDimension)); - configurations.SetProblemSize(data->GetLocations()->GetSize()); - configurations.SetEstimatedTheta(aEstimatedTheta); configurations.SetIsIDW(TRUE); - ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_values, train_locations, test_locations); - // Take back ownership, to avoid deleting apData when the unique_ptr goes out of scope. - auto temp_data = data.release(); - delete[] z_values; + PredictionSetupHelper(configurations, aKernelName, aDistanceMatrix, aDenseTileSize, aLowTileSize, aDimension, + aTrainData, aTestData, aEstimatedTheta, aTestMeasurementsValues); return Results::GetInstance()->GetIDWError(); } @@ -367,8 +231,7 @@ namespace exageostat::adapters { Nullable aLocationsY, Nullable aLocationsZ, unique_ptr > &aData, Configurations &aConfigurations, const string &aKernelName, const string &aDistanceMatrix, const int &aDenseTileSize, - const int &aLowTileSize, - const string &aDimension, const Computation &aComputation) { + const int &aLowTileSize, const string &aDimension, const Computation &aComputation) { double *pMeasurementsVectorPtr = nullptr; if (aMeasurementsVector == nullptr || aMeasurementsVector.isNull()) { @@ -432,4 +295,91 @@ namespace exageostat::adapters { aConfigurations.SetProblemSize(aData->GetLocations()->GetSize()); return pMeasurementsVectorPtr; } + + void ValidateDataDimensions(const vector > &aData, const string &aDataType) { + if (aData.empty() || aData.front().empty()) { + throw runtime_error("No " + aDataType + " data provided."); + } + + size_t expectedSize = aData.front().size(); + for (const auto &vec: aData) { + if (vec.size() != expectedSize) { + throw runtime_error("Inconsistent dimensions in " + aDataType + " data."); + } + } + } + + void + PredictionSetupHelper(Configurations &aConfigurations, const string &aKernelName, const string &aDistanceMatrix, + const int &aDenseTileSize, const int &aLowTileSize, const string &aDimension, + vector > &aTrainData, vector > &aTestData, + const vector &aEstimatedTheta, const vector &aTestMeasurementsValues) { + + aConfigurations.SetComputation(EXACT_DENSE); + + ValidateDataDimensions(aTrainData, "train"); + ValidateDataDimensions(aTestData, "test"); + + size_t potential_train_data_size = aTrainData[0].size(); + if (potential_train_data_size > static_cast(std::numeric_limits::max())) { + throw std::runtime_error("Train data size exceeds the maximum size for int type."); + } + int train_data_size = static_cast(potential_train_data_size); + + size_t potential_test_data_size = aTestData[0].size(); + if (potential_test_data_size > static_cast(std::numeric_limits::max())) { + throw std::runtime_error("Test data size exceeds the maximum size for int type."); + } + int test_data_size = static_cast(potential_test_data_size); + + auto data = make_unique>(train_data_size, aConfigurations.GetDimension()); + dataunits::Locations train_locations(train_data_size, aConfigurations.GetDimension()); + dataunits::Locations test_locations(test_data_size, aConfigurations.GetDimension()); + + // Allocate memory for z_values to hold elements from both sources + auto *z_values = new double[aTrainData.back().size() + aTestMeasurementsValues.size()]; + + for (int i = 0; i < train_data_size; i++) { + train_locations.SetLocationX(*aTrainData[0].data(), train_data_size); + train_locations.SetLocationY(*aTrainData[1].data(), train_data_size); + data->GetLocations()->SetLocationX(*aTrainData[0].data(), train_data_size); + data->GetLocations()->SetLocationY(*aTrainData[1].data(), train_data_size); + if (aTrainData.size() > 3) { + train_locations.SetLocationZ(*aTrainData[2].data(), train_data_size); + data->GetLocations()->SetLocationZ(*aTrainData[2].data(), train_data_size); + } + } + memcpy(z_values, aTrainData.back().data(), aTrainData.back().size() * sizeof(double)); + // Calculate the starting position for the next part of the data in z_values + auto *destination = z_values + aTrainData.back().size(); + // Copy data from aTestMeasurementsValues to the next part of z_values, after the previously copied data + memcpy(destination, aTestMeasurementsValues.data(), aTestMeasurementsValues.size() * sizeof(double)); + + for (int i = 0; i < test_data_size; i++) { + test_locations.SetLocationX(*aTestData[0].data(), test_data_size); + test_locations.SetLocationY(*aTestData[1].data(), test_data_size); + if (aTestData.size() > 2) { + test_locations.SetLocationZ(*aTestData[2].data(), test_data_size); + } + } + + // Set common configurations. + aConfigurations.CheckKernelValue(aKernelName); + aConfigurations.ParseDistanceMetric(aDistanceMatrix); + aConfigurations.SetDenseTileSize(aDenseTileSize); + aConfigurations.SetLowTileSize(aLowTileSize); + aConfigurations.SetDimension(Configurations::CheckDimensionValue(aDimension)); + aConfigurations.SetProblemSize(data->GetLocations()->GetSize()); + aConfigurations.SetEstimatedTheta(aEstimatedTheta); + + // Temporarily release ownership to pass to the function. + ExaGeoStatData *tempData = data.release(); + ExaGeoStat::ExaGeoStatPrediction(aConfigurations, + reinterpret_cast> &>(tempData), + z_values, &train_locations, &test_locations); + // Take back ownership + data.reset(tempData); + + delete[] z_values; + } } \ No newline at end of file diff --git a/src/Rcpp-adapters/RcppExports.cpp b/src/Rcpp-adapters/RcppExports.cpp index 97c0e9e0..a8ea7f88 100644 --- a/src/Rcpp-adapters/RcppExports.cpp +++ b/src/Rcpp-adapters/RcppExports.cpp @@ -31,8 +31,8 @@ RcppExport SEXP _rcpp_module_boot_ExaGeoStatCPP(); * @brief Array of R function call entries. */ static const R_CallMethodDef CallEntries[] = { - {"_rcpp_module_boot_ExaGeoStatCPP", (DL_FUNC) &_rcpp_module_boot_ExaGeoStatCPP, 0}, - {nullptr, nullptr, 0} + {"_rcpp_module_boot_ExaGeoStatCPP", (DL_FUNC) &_rcpp_module_boot_ExaGeoStatCPP, 0}, + {nullptr, nullptr, 0} }; /** diff --git a/src/Rcpp-adapters/RcppModules.cpp b/src/Rcpp-adapters/RcppModules.cpp index 60ed9691..c00edcb9 100644 --- a/src/Rcpp-adapters/RcppModules.cpp +++ b/src/Rcpp-adapters/RcppModules.cpp @@ -46,24 +46,32 @@ RCPP_MODULE(ExaGeoStatCPP) { function("model_data", &exageostat::adapters::R_ExaGeoStatModelData, List::create(_["computation"] = "exact", _["kernel"], _["distance_matrix"] = "euclidean", _["lb"], _["ub"], - _["tol"] = 4, _["mle_itr"], _["dts"], _["lts"] = 0, _["dimension"] = "2D",_["band"] = 0,_["max_rank"] = 500, _["data"] = R_NilValue, - _["matrix"] = R_NilValue, _["x"] = R_NilValue, _["y"] = R_NilValue, _["z"] = R_NilValue)); + _["tol"] = 4, _["mle_itr"], _["dts"], _["lts"] = 0, _["dimension"] = "2D", _["band"] = 0, + _["max_rank"] = 500, _["data"] = R_NilValue, _["matrix"] = R_NilValue, _["x"] = R_NilValue, + _["y"] = R_NilValue, _["z"] = R_NilValue)); function("predict_data", &exageostat::adapters::R_ExaGeoStatPredictData, - List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["dts"], _["lts"] = 0, _["dimension"] = "2D",_["train_data"], _["test_data"])); + List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["dts"], _["lts"] = 0, + _["dimension"] = "2D", _["train_data"], _["test_data"])); function("mloe_mmom", &exageostat::adapters::R_ExaGeoStatMLOE_MMOM, - List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["true_theta"], _["dts"], _["lts"] = 0, _["dimension"] = "2D", _["train_data"], _["test_data"])); + List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["true_theta"], + _["dts"], _["lts"] = 0, _["dimension"] = "2D", _["train_data"], _["test_data"])); function("fisher", &exageostat::adapters::R_ExaGeoStatFisher, - List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["dts"], _["lts"] = 0, _["dimension"] = "2D", _["train_data"], _["test_data"])); + List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["dts"], _["lts"] = 0, + _["dimension"] = "2D", _["train_data"], _["test_data"])); function("idw", &exageostat::adapters::R_ExaGeoStatIDW, - List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["dts"], _["lts"] = 0, _["dimension"] = "2D", _["train_data"], _["test_data"], _["test_measurements"])); + List::create(_["kernel"], _["distance_matrix"] = "euclidean", _["estimated_theta"], _["dts"], _["lts"] = 0, + _["dimension"] = "2D", _["train_data"], _["test_data"], _["test_measurements"])); function("get_locationsX", &exageostat::adapters::R_GetLocationX, List::create(_["data"])); + function("get_locationsY", &exageostat::adapters::R_GetLocationY, List::create(_["data"])); + function("get_locationsZ", &exageostat::adapters::R_GetLocationZ, List::create(_["data"])); + function("get_Z_measurement_vector", &exageostat::adapters::R_GetDescZValues, List::create(_["data"], _["type"])); } diff --git a/src/api/ExaGeoStat.cpp b/src/api/ExaGeoStat.cpp index 4a2d34c9..0af851c4 100644 --- a/src/api/ExaGeoStat.cpp +++ b/src/api/ExaGeoStat.cpp @@ -22,7 +22,6 @@ using namespace nlopt; using namespace exageostat::api; using namespace exageostat::generators; using namespace exageostat::dataunits; -using namespace exageostat::prediction; using namespace exageostat::configurations; template @@ -102,7 +101,7 @@ void ExaGeoStat::ExaGeoStatPrediction(Configurations &aConfigurations, std::u aConfigurations.GetTimeSlot()); // Add the data prediction arguments. aConfigurations.InitializeDataPredictionArguments(); - Prediction::PredictMissingData(aData, aConfigurations, apMeasurementsMatrix, *pKernel, apTrainLocations, - apTestLocations); + prediction::Prediction::PredictMissingData(aData, aConfigurations, apMeasurementsMatrix, *pKernel, + apTrainLocations, apTestLocations); delete pKernel; } \ No newline at end of file diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index e89844da..6d0c1586 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -13,7 +13,9 @@ **/ #ifdef USE_MPI + #include + #endif #include @@ -81,7 +83,7 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV, const int rank = 0; #ifdef USE_MPI - MPI_Init(&this->mArgC,&this->mpArgV); + MPI_Init(&this->mArgC, &this->mpArgV); MPI_Comm_rank(MPI_COMM_WORLD, &rank); #endif @@ -89,7 +91,7 @@ void Configurations::InitializeArguments(const int &aArgC, char **apArgV, const string example_name = apArgV[0]; // Remove the './' example_name.erase(0, 2); - if(!rank){ + if (!rank) { LOGGER("Running " + example_name) } string argument; @@ -612,7 +614,7 @@ int Configurations::CheckUnknownObservationsValue(const string &aValue) { void Configurations::ParseDistanceMetric(const std::string &aDistanceMetric) { if (aDistanceMetric == "eg" || aDistanceMetric == "EG" || aDistanceMetric == "euclidean") { SetDistanceMetric(EUCLIDEAN_DISTANCE); - } else if (aDistanceMetric == "gcd" || aDistanceMetric == "GCD"|| aDistanceMetric == "great_circle") { + } else if (aDistanceMetric == "gcd" || aDistanceMetric == "GCD" || aDistanceMetric == "great_circle") { SetDistanceMetric(GREAT_CIRCLE_DISTANCE); } else { throw range_error("Invalid value. Please use eg or gcd values only."); diff --git a/src/data-generators/DataGenerator.cpp b/src/data-generators/DataGenerator.cpp index 892d0fdd..761c5751 100644 --- a/src/data-generators/DataGenerator.cpp +++ b/src/data-generators/DataGenerator.cpp @@ -14,12 +14,10 @@ #include #include #include -#include using namespace exageostat::generators; using namespace exageostat::dataLoader::csv; using namespace exageostat::generators::synthetic; -using namespace exageostat::dataunits; using namespace exageostat::common; using namespace exageostat::results; diff --git a/src/data-generators/LocationGenerator.cpp b/src/data-generators/LocationGenerator.cpp index e1c25b8e..fcdff5f1 100644 --- a/src/data-generators/LocationGenerator.cpp +++ b/src/data-generators/LocationGenerator.cpp @@ -22,7 +22,9 @@ using namespace exageostat::common; using namespace exageostat::dataunits; using namespace exageostat::helpers; -template void LocationGenerator::GenerateLocations(const int &aN, const int &aTimeSlot, const Dimension &aDimension, Locations &aLocations) { +template +void LocationGenerator::GenerateLocations(const int &aN, const int &aTimeSlot, const Dimension &aDimension, + Locations &aLocations) { aLocations.SetSize(aN); int index = 0; @@ -88,7 +90,7 @@ T LocationGenerator::UniformDistribution(const T &aRangeLow, const T &aRangeH template void -LocationGenerator::SortLocations(const int &aN, const Dimension &aDimension, Locations &aLocations) { +LocationGenerator::SortLocations(const int &aN, const Dimension &aDimension, Locations &aLocations) { // Some sorting, required by spatial statistics code uint16_t x, y, z; @@ -96,10 +98,10 @@ LocationGenerator::SortLocations(const int &aN, const Dimension &aDimension, // Encode data into vector z for (auto i = 0; i < aN; i++) { - x = (uint16_t)(aLocations.GetLocationX()[i] * (double) UINT16_MAX + .5); - y = (uint16_t)(aLocations.GetLocationY()[i] * (double) UINT16_MAX + .5); + x = (uint16_t) (aLocations.GetLocationX()[i] * (double) UINT16_MAX + .5); + y = (uint16_t) (aLocations.GetLocationY()[i] * (double) UINT16_MAX + .5); if (aDimension != Dimension2D) { - z = (uint16_t)(aLocations.GetLocationZ()[i] * (double) UINT16_MAX + .5); + z = (uint16_t) (aLocations.GetLocationZ()[i] * (double) UINT16_MAX + .5); } else { z = (uint16_t) 0.0; } diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index e113b0a0..70c750a7 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -17,7 +17,6 @@ #include using namespace exageostat::generators::synthetic; -using namespace exageostat::dataunits; using namespace exageostat::common; using namespace exageostat::configurations; using namespace exageostat::results; @@ -40,7 +39,7 @@ SyntheticGenerator::CreateData(Configurations &aConfigurations, auto data = std::make_unique>(n, aConfigurations.GetDimension()); // Allocated new Locations object. - auto *locations = new Locations(n, aConfigurations.GetDimension()); + auto *locations = new dataunits::Locations(n, aConfigurations.GetDimension()); int parameters_number = aKernel.GetParametersNumbers(); // Set initial theta values. @@ -48,7 +47,8 @@ SyntheticGenerator::CreateData(Configurations &aConfigurations, aConfigurations.SetInitialTheta(aConfigurations.GetInitialTheta()); // Generate Locations phase - LocationGenerator::GenerateLocations(n, aConfigurations.GetTimeSlot(), aConfigurations.GetDimension(), *locations); + LocationGenerator::GenerateLocations(n, aConfigurations.GetTimeSlot(), aConfigurations.GetDimension(), + *locations); data->SetLocations(*locations); // Generate Descriptors phase @@ -62,12 +62,14 @@ SyntheticGenerator::CreateData(Configurations &aConfigurations, std::string path = aConfigurations.GetLoggerPath(); CHAMELEON_Desc2Lap(ChamUpperLower, data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc, pMatrix, aConfigurations.GetProblemSize()); - if (helpers::CommunicatorMPI::GetInstance()->GetRank() == 0){ - dataLoader::csv::CSVLoader::GetInstance()->WriteData(*((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc->mat), - aConfigurations.GetProblemSize(), parameters_number, path, - *data->GetLocations()); + DESCRIPTOR_Z).chameleon_desc, + pMatrix, aConfigurations.GetProblemSize()); + if (helpers::CommunicatorMPI::GetInstance()->GetRank() == 0) { + dataLoader::csv::CSVLoader::GetInstance()->WriteData( + *((T *) data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, + DESCRIPTOR_Z).chameleon_desc->mat), + aConfigurations.GetProblemSize(), parameters_number, path, + *data->GetLocations()); } delete[] pMatrix; #else diff --git a/src/data-loader/DataLoader.cpp b/src/data-loader/DataLoader.cpp index d1dc51f4..759980bc 100644 --- a/src/data-loader/DataLoader.cpp +++ b/src/data-loader/DataLoader.cpp @@ -13,12 +13,10 @@ **/ #include -#include using namespace std; using namespace exageostat::dataLoader; -using namespace exageostat::dataunits; using namespace exageostat::common; using namespace exageostat::results; diff --git a/src/data-loader/concrete/CSVLoader.cpp b/src/data-loader/concrete/CSVLoader.cpp index 661d7c53..e25bb057 100644 --- a/src/data-loader/concrete/CSVLoader.cpp +++ b/src/data-loader/concrete/CSVLoader.cpp @@ -19,10 +19,7 @@ using namespace std; using namespace exageostat::configurations; -using namespace exageostat::dataunits; -using namespace exageostat::kernels; using namespace exageostat::common; -using namespace exageostat::linearAlgebra; using namespace exageostat::dataLoader::csv; template diff --git a/src/data-units/DescriptorData.cpp b/src/data-units/DescriptorData.cpp index 1e7857b1..709c85ad 100644 --- a/src/data-units/DescriptorData.cpp +++ b/src/data-units/DescriptorData.cpp @@ -12,7 +12,6 @@ * @date 2023-07-18 **/ -#include #include using namespace exageostat::dataunits; @@ -28,7 +27,7 @@ DescriptorData::~DescriptorData() { for (const auto &pair: this->mDictionary) { const std::string &key = pair.first; if (key.find("CHAMELEON") != std::string::npos && pair.second != nullptr) { - exaGeoStatDescriptor.DestroyDescriptor(common::CHAMELEON_DESCRIPTOR, pair.second); + exaGeoStatDescriptor.DestroyDescriptor(CHAMELEON_DESCRIPTOR, pair.second); #ifdef USE_HICMA // Since there are converted descriptors from Chameleon to Hicma, which have the same memory address. // So, by deleting the owner which is Chameleon, no need to delete hicma. Therefore, we remove the row of that descriptor. @@ -41,7 +40,7 @@ DescriptorData::~DescriptorData() { } #endif } else if (key.find("HICMA") != std::string::npos && pair.second != nullptr) { - exaGeoStatDescriptor.DestroyDescriptor(common::HICMA_DESCRIPTOR, pair.second); + exaGeoStatDescriptor.DestroyDescriptor(HICMA_DESCRIPTOR, pair.second); } } this->mDictionary.clear(); @@ -140,7 +139,7 @@ DescriptorData::GetDescriptor(const DescriptorType &aDescriptorType, const De template void DescriptorData::SetDescriptor(const DescriptorType &aDescriptorType, const DescriptorName &aDescriptorName, - const bool &aIsOOC, void *apMatrix, const common::FloatPoint &aFloatPoint, + const bool &aIsOOC, void *apMatrix, const FloatPoint &aFloatPoint, const int &aMB, const int &aNB, const int &aSize, const int &aLM, const int &aLN, const int &aI, const int &aJ, const int &aM, const int &aN, const int &aP, const int &aQ, const bool &aValidOOC) { @@ -169,12 +168,13 @@ void DescriptorData::SetDescriptor(const DescriptorType &aDescriptorType, con } template -T *DescriptorData::GetDescriptorMatrix(const common::DescriptorType &aDescriptorType, void *apDesc) { - if (aDescriptorType == common::CHAMELEON_DESCRIPTOR) { - return (T *) ((CHAM_desc_t *) apDesc)->mat; +T * +DescriptorData::GetDescriptorMatrix(const DescriptorType &aDescriptorType, const DescriptorName &aDescriptorName) { + if (aDescriptorType == CHAMELEON_DESCRIPTOR) { + return (T *) (this->GetDescriptor(CHAMELEON_DESCRIPTOR, aDescriptorName).chameleon_desc)->mat; } else { #ifdef USE_HICMA - return (T *) ((HICMA_desc_t *) apDesc)->mat; + return (T *) (this->GetDescriptor(HICMA_DESCRIPTOR, aDescriptorName).hicma_desc)->mat; #else throw std::runtime_error("To use Hicma descriptor you need to enable USE_HICMA!"); #endif diff --git a/src/data-units/Locations.cpp b/src/data-units/Locations.cpp index 3e511f8a..65c1691c 100644 --- a/src/data-units/Locations.cpp +++ b/src/data-units/Locations.cpp @@ -12,13 +12,10 @@ * @date 2023-02-27 **/ -#include #include #include -using namespace std; - using namespace exageostat::dataunits; using namespace exageostat::common; diff --git a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp index c8ab42e2..7dd895ff 100644 --- a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp +++ b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp @@ -12,6 +12,9 @@ * @date 2023-07-17 **/ +#include + +#include #include #ifdef USE_HICMA @@ -20,8 +23,6 @@ #endif -#include - using namespace exageostat::common; using namespace exageostat::dataunits::descriptor; diff --git a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp index 9ba22f79..55f12abe 100644 --- a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp +++ b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp @@ -20,7 +20,8 @@ HICMA_desc_t *HicmaDescriptor::CreateHicmaDescriptor(void *apDescriptor, cons const common::FloatPoint &aFloatPoint, const int &aMB, const int &aNB, const int &aSize, const int &aLM, const int &aLN, const int &aI, const int &aJ, const int &aM, - const int &aN, const int &aP, const int &aQ, const bool &aValidOOC) { + const int &aN, const int &aP, const int &aQ, + const bool &aValidOOC) { auto hicma_desc = (HICMA_desc_t *) apDescriptor; if (aIsOOC && apMatrix == nullptr && aMB != 1 && aNB != 1 && aValidOOC) { HICMA_Desc_Create_OOC(&hicma_desc, (HICMA_enum) aFloatPoint, aMB, aNB, aSize, aLM, aLN, aI, aJ, aM, aN, aP, aQ); diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 6496900a..cd970c03 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -61,7 +61,7 @@ void ExaGeoStatHardware::InitHardware(const Computation &aComputation, const int exageostat::helpers::CommunicatorMPI::GetInstance()->SetHardwareInitialization(); } -void ExaGeoStatHardware::FinalizeHardware(){ +void ExaGeoStatHardware::FinalizeHardware() { // finalize hardware using Chameleon if (mpChameleonContext) { CHAMELEON_Finalize() @@ -79,18 +79,7 @@ void ExaGeoStatHardware::FinalizeHardware(){ ExaGeoStatHardware::~ExaGeoStatHardware() { Results::GetInstance()->PrintEndSummary(); - // finalize hardware using Chameleon - if (mpChameleonContext) { - CHAMELEON_Finalize() - mpChameleonContext = nullptr; - } - // finalize hardware using HiCMA -#ifdef USE_HICMA - if (mpHicmaContext) { - HICMA_Finalize(); - mpHicmaContext = nullptr; - } -#endif + FinalizeHardware(); exageostat::helpers::CommunicatorMPI::GetInstance()->RemoveHardwareInitialization(); } @@ -118,5 +107,5 @@ void *ExaGeoStatHardware::GetContext(Computation aComputation) { return nullptr; } -void * ExaGeoStatHardware::mpChameleonContext = nullptr; -void * ExaGeoStatHardware::mpHicmaContext = nullptr; \ No newline at end of file +void *ExaGeoStatHardware::mpChameleonContext = nullptr; +void *ExaGeoStatHardware::mpHicmaContext = nullptr; \ No newline at end of file diff --git a/src/helpers/CommunicatorMPI.cpp b/src/helpers/CommunicatorMPI.cpp index e527a8f9..26b156dc 100644 --- a/src/helpers/CommunicatorMPI.cpp +++ b/src/helpers/CommunicatorMPI.cpp @@ -23,12 +23,11 @@ CommunicatorMPI *CommunicatorMPI::GetInstance() { return mpInstance; } -int CommunicatorMPI::GetRank() const{ +int CommunicatorMPI::GetRank() const { #ifdef USE_MPI - if(!mIsHardwareInitialized){ + if (!mIsHardwareInitialized) { return 0; - } - else{ + } else { return CHAMELEON_Comm_rank(); } #else @@ -39,6 +38,7 @@ int CommunicatorMPI::GetRank() const{ void CommunicatorMPI::SetHardwareInitialization() { mIsHardwareInitialized = true; } + void CommunicatorMPI::RemoveHardwareInitialization() { mIsHardwareInitialized = false; } diff --git a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp index c1038762..ba1f0833 100644 --- a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp @@ -72,20 +72,23 @@ UnivariateMaternDdnuNu::GenerateCovarianceMatrix(T *apMatrixA, const int &aRo (pow(expr, aLocalTheta[2]) * log(expr) * gsl_sf_bessel_Knu(aLocalTheta[2], expr) + pow(expr, aLocalTheta[2]) * - BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], - expr))); + BasselFunction::CalculateDerivativeBesselNu( + aLocalTheta[2], + expr))); nu_expr_dprime = (1 - aLocalTheta[2]) * 1 / pow(2, aLocalTheta[2]) * 1 / tgamma(aLocalTheta[2]) * - pow(expr, aLocalTheta[2]) * BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr) + + pow(expr, aLocalTheta[2]) * + BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr) + pow(2, 1 - aLocalTheta[2]) * (-1 / tgamma(aLocalTheta[2]) * gsl_sf_psi(aLocalTheta[2]) * pow(expr, aLocalTheta[2]) * - BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr) + 1 / tgamma(aLocalTheta[2]) * - (pow(expr, aLocalTheta[2]) * - log(expr) * - BasselFunction::CalculateDerivativeBesselNu( - aLocalTheta[2], - expr) + - pow(expr, aLocalTheta[2]) * - BasselFunction::CalculateSecondDerivativeBesselNu(aLocalTheta[2],expr))); + BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr) + + 1 / tgamma(aLocalTheta[2]) * + (pow(expr, aLocalTheta[2]) * + log(expr) * + BasselFunction::CalculateDerivativeBesselNu( + aLocalTheta[2], + expr) + + pow(expr, aLocalTheta[2]) * + BasselFunction::CalculateSecondDerivativeBesselNu(aLocalTheta[2], expr))); apMatrixA[i + j * aRowsNumber] = (-0.5 * con * pow(expr, aLocalTheta[2]) * gsl_sf_bessel_Knu(aLocalTheta[2], expr) + (1 - aLocalTheta[2]) / 2 * nu_expr - diff --git a/src/kernels/concrete/UnivariateMaternDnu.cpp b/src/kernels/concrete/UnivariateMaternDnu.cpp index e9e642ce..4d8ee30a 100644 --- a/src/kernels/concrete/UnivariateMaternDnu.cpp +++ b/src/kernels/concrete/UnivariateMaternDnu.cpp @@ -68,7 +68,8 @@ void UnivariateMaternDnu::GenerateCovarianceMatrix(T *apMatrixA, const int &a (pow(expr, aLocalTheta[2]) * log(expr) * gsl_sf_bessel_Knu(aLocalTheta[2], expr) + pow(expr, aLocalTheta[2]) * - BasselFunction::CalculateDerivativeBesselNu(aLocalTheta[2], expr))); + BasselFunction::CalculateDerivativeBesselNu( + aLocalTheta[2], expr))); apMatrixA[i + j * aRowsNumber] = sigma_square * nu_expr; } j0++; diff --git a/src/kernels/concrete/UnivariatePowExpStationary.cpp b/src/kernels/concrete/UnivariatePowExpStationary.cpp index 91339311..1563025a 100644 --- a/src/kernels/concrete/UnivariatePowExpStationary.cpp +++ b/src/kernels/concrete/UnivariatePowExpStationary.cpp @@ -58,7 +58,8 @@ UnivariatePowExpStationary::GenerateCovarianceMatrix(T *apMatrixA, const int dist = DistanceCalculationHelpers::CalculateDistance(aLocation1, aLocation2, i0, j0, aDistanceMetric, flag); dist = pow(dist, nu); - *(apMatrixA + i + j * aRowsNumber) = (dist == 0.0) ? sigma_square : sigma_square * exp(-(dist / aLocalTheta[1])); + *(apMatrixA + i + j * aRowsNumber) = (dist == 0.0) ? sigma_square : sigma_square * + exp(-(dist / aLocalTheta[1])); j0++; } i0++; diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index 335167a0..9b73f1a3 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -13,7 +13,9 @@ **/ #ifdef USE_MPI + #include + #endif #include @@ -182,7 +184,8 @@ void LinearAlgebraMethods::InitiateFisherDescriptors(Configurations &aConfigu } template -void LinearAlgebraMethods::InitiatePredictionDescriptors(Configurations &aConfigurations, std::unique_ptr> &aData) { +void LinearAlgebraMethods::InitiatePredictionDescriptors(Configurations &aConfigurations, + std::unique_ptr> &aData) { if (!ExaGeoStatHardware::GetChameleonContext()) { throw std::runtime_error( @@ -414,7 +417,7 @@ void LinearAlgebraMethods::GenerateObservationsVector(Configurations &aConfig pMatrix = new T[full_problem_size]; string path = aConfigurations.GetLoggerPath(); ExaGeoStatDesc2Lap(pMatrix, full_problem_size, CHAM_descZ, EXAGEOSTAT_UPPER_LOWER); - if ( CHAMELEON_Comm_rank() == 0 ){ + if (CHAMELEON_Comm_rank() == 0) { dataLoader::csv::CSVLoader::GetInstance()->WriteData(*pMatrix, full_problem_size, P, path, *apLocation1); } delete[] pMatrix; @@ -475,11 +478,11 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptrGetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_Z_OBSERVATIONS).chameleon_desc; - T *mspe = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe); + T *mspe = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE); *mspe = 0; - T *mspe_1 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe1); + T *mspe_1 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE_1); *mspe_1 = 0; - T *mspe_2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe2); + T *mspe_2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE_2); *mspe_2 = 0; auto kernel_name = aConfiguration.GetKernelName(); @@ -582,7 +585,7 @@ T *LinearAlgebraMethods::ExaGeoStatMLEPredictTile(std::unique_ptr::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< auto *CHAM_desc_Rcopy = aData->GetDescriptorData()->GetDescriptor(common::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_R_COPY).chameleon_desc; - T *mspe = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe); + T *mspe = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE); *mspe = 0; - T *mspe_1 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe1); + T *mspe_1 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE_1); *mspe_1 = 0; - T *mspe_2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mspe2); + T *mspe_2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_MSPE_2); *mspe_2 = 0; auto kernel_name = aConfiguration.GetKernelName(); @@ -731,8 +734,8 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_desc_Rcopy, CHAM_desc_R, 0, CHAM_desc_C12); STOP_TIMING(time_gemm); - auto r = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_C12); - auto Zmiss2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_Zmiss); + auto r = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C12); + auto Zmiss2 = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_MISS); auto ng_nu = new T[aZMissNumber]; auto ng_sigma_sq = new T[aZMissNumber]; @@ -792,6 +795,7 @@ T *LinearAlgebraMethods::ExaGeoStatMLENonGaussianPredictTile(std::unique_ptr< all_mspe[2] = *mspe_2; delete[] ng_sigma_sq; delete[] ng_nu; + return all_mspe; } @@ -854,9 +858,9 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu auto *CHAM_desc_truth_alpha = aData->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_TRUTH_ALPHA).chameleon_desc; - T *mloe = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mloe); + T *mloe = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_MLOE); *mloe = 0; - T *mmom = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_mmom); + T *mmom = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_MMOM); *mmom = 0; // Create a Chameleon sequence, if not initialized before through the same descriptors @@ -1090,12 +1094,12 @@ void LinearAlgebraMethods::ExaGeoStatMLETileMLOEMMOM(Configurations &aConfigu ExaGeoStatGeaddTile(EXAGEOSTAT_NO_TRANS, 1, CHAM_desc_estimated_alpha, -1, CHAM_desc_expr4); VERBOSE("\t- Matrix Generation Time: " << matrix_gen << " Vectors Generation Time: " << vecs_gen - << " First Cholesky factorization Time: " << cholesky1 - << " First Cholesky factorization Time: " << cholesky2) + << " First Cholesky factorization Time: " << cholesky1 + << " First Cholesky factorization Time: " << cholesky2) VERBOSE("\t- First Trsm time: " << trsm1 << " Second Trsm time: " << trsm2 << " Third Trsm time: " << trsm3 - << " Fourth Trsm time: " << trsm4) + << " Fourth Trsm time: " << trsm4) VERBOSE("\t- First gemm time: " << gevv1 << " Second gemm time: " << gevv2 << " Third gemm time: " << gevv3 - << " Fourth gemm time: " << gevv4 << " Fifth gemm time: " << gevv5) + << " Fourth gemm time: " << gevv4 << " Fifth gemm time: " << gevv5) RuntimeFunctions::ExaGeoStatMLETileAsyncMLOEMMOM(CHAM_desc_expr2, CHAM_desc_expr3, CHAM_desc_expr4, CHAM_desc_mloe, CHAM_desc_mmom, sequence, request); this->ExaGeoStatSequenceWait(sequence); @@ -1148,7 +1152,7 @@ T *LinearAlgebraMethods::ExaGeoStatFisherTile(Configurations &aConfigurations auto *CHAM_desc_C_trace = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_C_TRACE).chameleon_desc; - auto trace = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_C_trace); + auto trace = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_C_TRACE); *trace = 0.0; RUNTIME_request_t request_array[2] = {RUNTIME_REQUEST_INITIALIZER, RUNTIME_REQUEST_INITIALIZER}; @@ -1329,7 +1333,7 @@ LinearAlgebraMethods::ExaGeoStatGetZObs(Configurations &aConfigurations, T *a z_desc = (CHAM_desc_t *) aDescData.GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z_COPY).chameleon_desc; } double epsilon = 1e-8; - if (abs(((T *)z_desc->mat)[0]) < epsilon) { + if (abs(((T *) z_desc->mat)[0]) < epsilon) { z_desc = (CHAM_desc_t *) aDescData.GetDescriptor(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z).chameleon_desc; } this->ExaGeoStatDesc2Lap(apZ, aSize, z_desc, UpperLower::EXAGEOSTAT_UPPER_LOWER); diff --git a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp index 0b1d33f7..47857c7d 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp @@ -14,7 +14,9 @@ **/ #ifdef USE_MPI + #include + #endif #include @@ -93,13 +95,13 @@ T ChameleonImplementation::ExaGeoStatMLETile(std::unique_ptrGetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_PRODUCT_3).chameleon_desc; - T *determinant = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_det); + T *determinant = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_DETERMINANT); *determinant = 0; - T *product = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_product); + T *product = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT); *product = 0; T *sum; if (aConfigurations.GetIsNonGaussian()) { - sum = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_sum); + sum = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_SUM); *sum = 0; } n = CHAM_desc_C->m; @@ -263,9 +265,9 @@ T ChameleonImplementation::ExaGeoStatMLETile(std::unique_ptr + #ifdef USE_MPI + #include + #endif #include @@ -58,8 +62,7 @@ void HicmaImplementation::SetModelingDescriptors(std::unique_ptrGetDescriptorData()->SetDescriptor(common::HICMA_DESCRIPTOR, DESCRIPTOR_CD, is_OOC, nullptr, float_point, - MBD, - NBD, MBD * NBD, MD, ND, 0, 0, MD, ND, p_grid, q_grid); + MBD, NBD, MBD * NBD, MD, ND, 0, 0, MD, ND, p_grid, q_grid); int MBUV = lts; int NBUV = 2 * max_rank; int MUV; @@ -158,17 +161,15 @@ T HicmaImplementation::ExaGeoStatMLETile(std::unique_ptr> & DescriptorName::DESCRIPTOR_DETERMINANT).hicma_desc; auto *CHAM_desc_product = aData->GetDescriptorData()->GetDescriptor(DescriptorType::CHAMELEON_DESCRIPTOR, DescriptorName::DESCRIPTOR_PRODUCT).chameleon_desc; - auto *HICMA_desc_product = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, - DescriptorName::DESCRIPTOR_PRODUCT).chameleon_desc; auto *HICMA_desc_sum = aData->GetDescriptorData()->GetDescriptor(DescriptorType::HICMA_DESCRIPTOR, DescriptorName::DESCRIPTOR_SUM).chameleon_desc; N = HICMA_descCUV->m; NRHS = HICMA_descZ->n; lts = HICMA_descZ->mb; - T *determinant = aData->GetDescriptorData()->GetDescriptorMatrix(HICMA_DESCRIPTOR, HICMA_desc_det); + T *determinant = aData->GetDescriptorData()->GetDescriptorMatrix(HICMA_DESCRIPTOR, DESCRIPTOR_DETERMINANT); *determinant = 0; - T *product = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, CHAM_desc_product); + T *product = aData->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_PRODUCT); *product = 0; T *sum; @@ -246,19 +247,17 @@ T HicmaImplementation::ExaGeoStatMLETile(std::unique_ptr> & ExaGeoStatSequenceWait(pSequence); VERBOSE(" Done.") - sum = aData->GetDescriptorData()->GetDescriptorMatrix(HICMA_DESCRIPTOR, HICMA_desc_sum); + sum = aData->GetDescriptorData()->GetDescriptorMatrix(HICMA_DESCRIPTOR, DESCRIPTOR_SUM); *sum = 0; VERBOSE("LR:Calculating dot product...") CHAMELEON_dgemm_Tile(ChamTrans, ChamNoTrans, 1, CHAM_descZ, CHAM_descZ, 0, CHAM_desc_product); VERBOSE(" Done.") +#ifdef USE_MPI double global_sum; double local_sum = *product; -#ifdef USE_MPI MPI_Allreduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); -#else - global_sum = *product; #endif RuntimeFunctions::ExaGeoStatNonGaussianLogLikeTileAsync(aConfigurations.GetComputation(), HICMA_descZ, HICMA_desc_sum, (T *) theta, pSequence, @@ -344,6 +343,9 @@ T HicmaImplementation::ExaGeoStatMLETile(std::unique_ptr> & Results::GetInstance()->SetLogLikValue(loglik); aConfigurations.SetEstimatedTheta(aConfigurations.GetStartingTheta()); + // Due to a leak in HiCMA in ZGEMM, We had to free the buffer manually. + mkl_free_buffers(); + return loglik; } diff --git a/src/prediction/Prediction.cpp b/src/prediction/Prediction.cpp index 813ccdb6..b3967ac6 100644 --- a/src/prediction/Prediction.cpp +++ b/src/prediction/Prediction.cpp @@ -13,6 +13,7 @@ **/ #include +#include #include #include @@ -51,12 +52,11 @@ void Prediction::PredictMissingData(unique_ptr> &aData, Con int p = aKernel.GetVariablesNumber(); int z_miss_number, n_z_obs; T *z_actual; - if(!apTrainLocations && !apTestLocations){ + if (!apTrainLocations && !apTestLocations) { z_miss_number = aConfigurations.GetUnknownObservationsNb(); n_z_obs = aConfigurations.CalculateZObsNumber(); z_actual = new T[z_miss_number * p]; - } - else{ + } else { z_miss_number = apTestLocations->GetSize(); n_z_obs = apTrainLocations->GetSize(); z_actual = nullptr; @@ -139,7 +139,7 @@ void Prediction::PredictMissingData(unique_ptr> &aData, Con LOGGER("\t---- Using Auxiliary Function IDW ----") T *mspe = new T[number_of_mspe]; - if(!z_actual){ + if (!z_actual) { z_actual = new T[z_miss_number * p]; memcpy(z_actual, apMeasurementsMatrix + n_z_obs, z_miss_number * sizeof(T)); } @@ -186,12 +186,16 @@ void Prediction::PredictMissingData(unique_ptr> &aData, Con z_miss_vector.push_back(z_miss[idx]); } Results::GetInstance()->SetPredictedMissedValues(z_miss_vector); - if(z_actual){ + if (z_actual) { LOGGER("\t\t- MSPE: " << avg_pred_value[0]) } delete[] prediction_error_mspe; } + + // Due to a leak in Chameleon, exactly trsm We had to free the buffer manually. + mkl_free_buffers(); + delete[] z_obs; delete[] z_miss; delete[] z_actual; @@ -212,10 +216,10 @@ void Prediction::InitializePredictionArguments(Configurations &aConfiguration aLinearAlgebraSolver->ExaGeoStatGetZObs(aConfigurations, z, full_problem_size, *aData->GetDescriptorData(), apMeasurementsMatrix, aP); - if(!apTrainLocations && !apTestLocations){ - PredictionHelpers::PickRandomPoints(aConfigurations, aData, apZObs, apZActual, z, aMissLocation, aObsLocation, aP); - } - else{ + if (!apTrainLocations && !apTestLocations) { + PredictionHelpers::PickRandomPoints(aConfigurations, aData, apZObs, apZActual, z, aMissLocation, + aObsLocation, aP); + } else { for (int i = 0; i < apTrainLocations->GetSize(); ++i) { aObsLocation.GetLocationX()[i] = apTrainLocations->GetLocationX()[i]; aObsLocation.GetLocationY()[i] = apTrainLocations->GetLocationY()[i]; diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt index dfcb8f10..e1ea8712 100644 --- a/src/runtime/CMakeLists.txt +++ b/src/runtime/CMakeLists.txt @@ -21,5 +21,5 @@ endif () set(SOURCES ${SOURCES} PARENT_SCOPE -) + ) diff --git a/src/runtime/parsec/CMakeLists.txt b/src/runtime/parsec/CMakeLists.txt index 904c01c6..24ed6d70 100644 --- a/src/runtime/parsec/CMakeLists.txt +++ b/src/runtime/parsec/CMakeLists.txt @@ -14,4 +14,4 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ParsecFunctions.cpp ${SOURCES} PARENT_SCOPE -) \ No newline at end of file + ) \ No newline at end of file diff --git a/src/runtime/starpu/CMakeLists.txt b/src/runtime/starpu/CMakeLists.txt index 23944963..f2af4f73 100644 --- a/src/runtime/starpu/CMakeLists.txt +++ b/src/runtime/starpu/CMakeLists.txt @@ -21,7 +21,7 @@ set(SOURCES ${ALL_CODELETS} ${SOURCES} PARENT_SCOPE -) + ) # Automatically add new codelets header files to StarPuCodeletsHeaders.hpp @@ -47,9 +47,9 @@ set(GENERATED_INCLUDES "${PATH_TO_CODELETS_HEADERS}/StarPuCodeletsHeaders.hpp") file(GLOB_RECURSE HEADER_FILES "${PATH_TO_CODELETS_HEADERS}/concrete/*.hpp") file(WRITE ${GENERATED_INCLUDES} ${DOCUMENTATION_STRING}) -foreach(HEADER_FILE ${HEADER_FILES}) +foreach (HEADER_FILE ${HEADER_FILES}) # Construct the include directive by stripping the known base directory part from the full path string(REPLACE "${PATH_TO_CODELETS_HEADERS}" "" HEADER_RELATIVE_PATH ${HEADER_FILE}) # Use angle brackets and the desired base path for the include directive file(APPEND ${GENERATED_INCLUDES} "#include \n") -endforeach() +endforeach () diff --git a/src/runtime/starpu/concrete/dcmg-codelet.cpp b/src/runtime/starpu/concrete/dcmg-codelet.cpp index 3ed51f80..a70053b1 100644 --- a/src/runtime/starpu/concrete/dcmg-codelet.cpp +++ b/src/runtime/starpu/concrete/dcmg-codelet.cpp @@ -46,7 +46,7 @@ void DCMGCodelet::InsertTask(void *apDescriptor, const int &aTriangularPart, for (col = 0; col < CHAM_apDescriptor->nt; col++) { cols_num = col == CHAM_apDescriptor->nt - 1 ? CHAM_apDescriptor->n - col * CHAM_apDescriptor->nb - : CHAM_apDescriptor->nb; + : CHAM_apDescriptor->nb; if (aTriangularPart == ChamUpperLower) { row = 0; } else { @@ -54,7 +54,7 @@ void DCMGCodelet::InsertTask(void *apDescriptor, const int &aTriangularPart, } for (; row < CHAM_apDescriptor->mt; row++) { rows_num = row == CHAM_apDescriptor->mt - 1 ? CHAM_apDescriptor->m - row * CHAM_apDescriptor->mb - : CHAM_apDescriptor->mb; + : CHAM_apDescriptor->mb; tile_row = row * CHAM_apDescriptor->mb; tile_col = col * CHAM_apDescriptor->nb; starpu_insert_task(&this->cl_dcmg, @@ -82,7 +82,9 @@ void DCMGCodelet::cl_dcmg_function(void *apBuffers[], void *apCodeletArgument Kernel *pKernel; pDescriptor_A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - starpu_codelet_unpack_args(apCodeletArguments, &rows_num, &cols_num, &tile_row, &tile_col, &pLocation1, &pLocation2, &pLocation3, &pLocal_theta, + starpu_codelet_unpack_args(apCodeletArguments, &rows_num, &cols_num, &tile_row, &tile_col, &pLocation1, &pLocation2, + &pLocation3, &pLocal_theta, &distance_metric, &pKernel); - pKernel->GenerateCovarianceMatrix(pDescriptor_A, rows_num, cols_num, tile_row, tile_col, *pLocation1, *pLocation2, *pLocation3, pLocal_theta, distance_metric); + pKernel->GenerateCovarianceMatrix(pDescriptor_A, rows_num, cols_num, tile_row, tile_col, *pLocation1, *pLocation2, + *pLocation3, pLocal_theta, distance_metric); } diff --git a/src/runtime/starpu/concrete/ddotp-codelet.cpp b/src/runtime/starpu/concrete/ddotp-codelet.cpp index b8047a28..d14c50cd 100644 --- a/src/runtime/starpu/concrete/ddotp-codelet.cpp +++ b/src/runtime/starpu/concrete/ddotp-codelet.cpp @@ -48,12 +48,12 @@ void DDOTPCodelet::InsertTask(void *apDescA, void *apDescProduct) { auto desc_m = pDesc_A->m; auto desc_mb = pDesc_A->mb; - for ( row = 0; row < desc_mt; row++) { + for (row = 0; row < desc_mt; row++) { rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; starpu_insert_task(&this->cl_ddotp, STARPU_VALUE, &rows_num, sizeof(int), - STARPU_RW,(starpu_data_handle_t) RUNTIME_data_getaddr(pDesc_product, 0, 0), + STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr(pDesc_product, 0, 0), STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr(pDesc_A, row, 0), 0); } diff --git a/src/runtime/starpu/concrete/dmdet-codelet.cpp b/src/runtime/starpu/concrete/dmdet-codelet.cpp index 25a0648c..d6a65bbc 100644 --- a/src/runtime/starpu/concrete/dmdet-codelet.cpp +++ b/src/runtime/starpu/concrete/dmdet-codelet.cpp @@ -45,31 +45,21 @@ void DMDETCodelet::InsertTask(const Computation &aComputation, void *apDescA, auto desc_m = aStarPuHelpers->GetM(apDescA); auto desc_mb = aStarPuHelpers->GetMB(apDescA); - if (aComputation == DIAGONAL_APPROX || aComputation == EXACT_DENSE) { - for (row = 0; row < desc_mt; row++) { - rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; - starpu_insert_task(&this->cl_dmdet, - STARPU_VALUE, &rows_num, sizeof(int), - STARPU_R, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescA, row, row), - STARPU_RW, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescDet, 0, 0), - 0); - } - } else if (aComputation == TILE_LOW_RANK) { - for (row = 0; row < desc_mt; row++) { - rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; - starpu_insert_task(&this->cl_dmdet, - STARPU_VALUE, &rows_num, sizeof(int), - STARPU_R, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescA, row, 0), - STARPU_RW, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescDet, 0, 0), - 0); - } + for (row = 0; row < desc_mt; row++) { + rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; + starpu_insert_task(&this->cl_dmdet, + STARPU_VALUE, &rows_num, sizeof(int), + STARPU_R, + aStarPuHelpers->ExaGeoStatDataGetAddr(apDescA, row, aComputation != TILE_LOW_RANK ? row : 0), + STARPU_RW, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescDet, 0, 0), + 0); } } template void DMDETCodelet::cl_dmdet_function(void *apBuffers[], void *apCodeletArguments) { int rows_num; - T *pDescriptor_A, *pDeterminant ; + T *pDescriptor_A, *pDeterminant; pDescriptor_A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); pDeterminant = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); diff --git a/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp b/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp index 5409c8a8..77e02c29 100644 --- a/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp +++ b/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp @@ -44,21 +44,26 @@ void DmloeMmomCodelet::InsertTask(void *apDescExpr1, void *apDescExpr2, void for (col = 0; col < ((CHAM_desc_t *) apDescExpr1)->nt; col++) { cols_num = col == ((CHAM_desc_t *) apDescExpr1)->nt - 1 ? ((CHAM_desc_t *) apDescExpr1)->n - - col * ((CHAM_desc_t *) apDescExpr1)->nb + col * ((CHAM_desc_t *) apDescExpr1)->nb : ((CHAM_desc_t *) apDescExpr1)->nb; for (row = 0; row < ((CHAM_desc_t *) apDescExpr1)->mt; row++) { rows_num = row == ((CHAM_desc_t *) apDescExpr1)->mt - 1 ? ((CHAM_desc_t *) apDescExpr1)->m - - row * ((CHAM_desc_t *) apDescExpr1)->mb + row * ((CHAM_desc_t *) apDescExpr1)->mb : ((CHAM_desc_t *) apDescExpr1)->mb; starpu_insert_task(&this->cl_dmloe_mmom, STARPU_VALUE, &rows_num, sizeof(int), STARPU_VALUE, &cols_num, sizeof(int), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr1, row, col), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr2, row, col), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr3, row, col), - STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescMLOE, row, col), - STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescMMOM, row, col), + STARPU_R, + (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr1, row, col), + STARPU_R, + (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr2, row, col), + STARPU_R, + (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr3, row, col), + STARPU_RW, + (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescMLOE, row, col), + STARPU_RW, + (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescMMOM, row, col), 0); } } diff --git a/src/runtime/starpu/concrete/dmse-codelet.cpp b/src/runtime/starpu/concrete/dmse-codelet.cpp index 6240fe80..2bcace90 100644 --- a/src/runtime/starpu/concrete/dmse-codelet.cpp +++ b/src/runtime/starpu/concrete/dmse-codelet.cpp @@ -43,11 +43,13 @@ void DMSECodelet::InsertTask(void *apDescError, void *apDescZPredict, void *a auto pDesc_Z_predict = (CHAM_desc_t *) apDescZPredict; for (row = 0; row < pDesc_Z_predict->mt; row++) { - rows_num = row == pDesc_Z_predict->mt - 1 ? pDesc_Z_predict->m - row * pDesc_Z_predict->mb : pDesc_Z_predict->mb; + rows_num = + row == pDesc_Z_predict->mt - 1 ? pDesc_Z_predict->m - row * pDesc_Z_predict->mb : pDesc_Z_predict->mb; starpu_insert_task(&this->cl_dmse, STARPU_VALUE, &rows_num, sizeof(int), STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescError, 0, 0), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZPredict, row, 0), + STARPU_R, + (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZPredict, row, 0), STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZMiss, row, 0), 0); } diff --git a/src/runtime/starpu/concrete/dtrace-codelet.cpp b/src/runtime/starpu/concrete/dtrace-codelet.cpp index 31e6cb5b..a5ecdda7 100644 --- a/src/runtime/starpu/concrete/dtrace-codelet.cpp +++ b/src/runtime/starpu/concrete/dtrace-codelet.cpp @@ -56,7 +56,7 @@ void DTRACECodelet::InsertTask(void *apDescA, void *apDescNum, void *apDescTr template void DTRACECodelet::cl_dtrace_function(void *apBuffers[], void *apCodeletArguments) { int rows_num; - T *pDescriptor_A, *pSum , *pTrace; + T *pDescriptor_A, *pSum, *pTrace; pDescriptor_A = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); pSum = (T *) STARPU_MATRIX_GET_PTR(apBuffers[1]); diff --git a/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp b/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp index e0127324..af873b51 100644 --- a/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp +++ b/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp @@ -63,7 +63,8 @@ void GaussianCodelet::cl_gaussian_to_non_function(void **apBuffers, void *apC pTheta = new T[6]; pDescriptorZ = (T *) STARPU_MATRIX_GET_PTR(apBuffers[0]); - starpu_codelet_unpack_args(apCodeletArguments, &rows_num, &pTheta[0], &pTheta[1], &pTheta[2], &pTheta[3], &pTheta[4], + starpu_codelet_unpack_args(apCodeletArguments, &rows_num, &pTheta[0], &pTheta[1], &pTheta[2], &pTheta[3], + &pTheta[4], &pTheta[5]); //core function to convert Z tile from Gaussian to non-Gaussian. core_gaussian_to_non(pDescriptorZ, pTheta, rows_num); @@ -71,7 +72,7 @@ void GaussianCodelet::cl_gaussian_to_non_function(void **apBuffers, void *apC } template -void GaussianCodelet::core_gaussian_to_non(T *apDescriptorZ,const T *apLocalTheta, const int &aSize) { +void GaussianCodelet::core_gaussian_to_non(T *apDescriptorZ, const T *apLocalTheta, const int &aSize) { T xi = apLocalTheta[2]; T omega = apLocalTheta[3]; @@ -87,7 +88,8 @@ void GaussianCodelet::core_gaussian_to_non(T *apDescriptorZ,const T *apLocalT apDescriptorZ[i] = xi + omega * apDescriptorZ[i] * (exp(0.5 * h * pow(apDescriptorZ[i], 2))); } else { for (i = 0; i < aSize; i++) - apDescriptorZ[i] = xi + omega * (exp(g * apDescriptorZ[i]) - 1) * (exp(0.5 * h * pow(apDescriptorZ[i], 2))) / g; + apDescriptorZ[i] = + xi + omega * (exp(g * apDescriptorZ[i]) - 1) * (exp(0.5 * h * pow(apDescriptorZ[i], 2))) / g; } } diff --git a/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp b/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp index cb38b2e3..d0dbdac6 100644 --- a/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp +++ b/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp @@ -78,7 +78,8 @@ void NonGaussianLoglike::cl_non_gaussian_loglike_function(void **apBuffers, v } template -double NonGaussianLoglike::core_non_gaussian_loglike_helper(const T *apDescriptorZ, const T *apLocalTheta, const int &aSize) { +double NonGaussianLoglike::core_non_gaussian_loglike_helper(const T *apDescriptorZ, const T *apLocalTheta, + const int &aSize) { T g = apLocalTheta[4]; T h = apLocalTheta[5]; @@ -92,7 +93,8 @@ double NonGaussianLoglike::core_non_gaussian_loglike_helper(const T *apDescri if (g == 0) sum += log(1 + h * pow(apDescriptorZ[i], 2)) + 0.5 * h * pow(apDescriptorZ[i], 2); else { - sum += log(exp(g * apDescriptorZ[i]) + (exp(g * apDescriptorZ[i]) - 1) * h * apDescriptorZ[i] / g) + 0.5 * h * pow(apDescriptorZ[i], 2); + sum += log(exp(g * apDescriptorZ[i]) + (exp(g * apDescriptorZ[i]) - 1) * h * apDescriptorZ[i] / g) + + 0.5 * h * pow(apDescriptorZ[i], 2); } } return sum; diff --git a/src/runtime/starpu/helpers/CMakeLists.txt b/src/runtime/starpu/helpers/CMakeLists.txt index c18f49a2..63510dd9 100644 --- a/src/runtime/starpu/helpers/CMakeLists.txt +++ b/src/runtime/starpu/helpers/CMakeLists.txt @@ -14,7 +14,7 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/StarPuHelpersFactory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/concrete/ChameleonStarPuHelpers.cpp ${SOURCES} -) + ) if (USE_HICMA) list(APPEND SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/concrete/HicmaStarPuHelpers.cpp) diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index d3170eff..0dffd0c3 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -18,7 +18,7 @@ add_subdirectory(linear-algebra-solvers) add_subdirectory(prediction) add_subdirectory(results) -if(USE_HICMA) +if (USE_HICMA) add_subdirectory(data-units) endif () diff --git a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp index 15cd582d..4f8b22f7 100644 --- a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp +++ b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp @@ -22,150 +22,148 @@ using namespace std; using namespace exageostat::adapters; +using namespace exageostat::common; + +void TEST_DATA_GENERATION() { + + const string computation = "exact"; + const string dimension = "2D"; + const string kernel = "univariate_matern_stationary"; + const string distance_matrix = "eg"; + const int cores_number = 1; + const int gpus_number = 0; + const int problem_size = 9; + const int dts = 3; + const int lts = 0; + const vector initial_theta = {1, 0.1, 0.5}; + const vector lower_bound = {0.1, 0.1, 0.1}; + const vector upper_bound = {5, 5, 5}; + const vector estimated_theta = {0.9, 0.2, 0.5}; + srand(0); + + auto hardware = ExaGeoStatHardware(computation, cores_number, gpus_number); + auto exageostat_data = R_ExaGeoStatLoadData(kernel, initial_theta, distance_matrix, problem_size, 0, dts, lts, + dimension, "", "", "", ""); + + vector x = {0.257389, 0.456062, 0.797269, 0.242161, 0.440742, 0.276432, 0.493965, 0.953933, 0.86952}; + vector y = {0.138506, 0.238193, 0.170245, 0.579583, 0.514397, 0.752682, 0.867704, 0.610986, 0.891279}; + vector z = {-1.27234, -2.47547, 0.54585, -0.120985, 0.242569, -1.54421, 0.0986468, 0.779835, -1.48139}; + + for (int i = 0; i < problem_size; i++) { + REQUIRE((exageostat_data->GetLocations()->GetLocationX()[i] - x[i]) == Catch::Approx(0.0).margin(1e-6)); + REQUIRE((exageostat_data->GetLocations()->GetLocationY()[i] - y[i]) == Catch::Approx(0.0).margin(1e-6)); + REQUIRE((exageostat_data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z)[i] - + z[i]) == Catch::Approx(0.0).margin(1e-5)); + } + delete exageostat_data; +} void TEST_ALL_R_METHODS() { + const string computation = "exact"; const int cores_number = 1; const int gpus_number = 0; + const string dimension = "2D"; + const string kernel = "univariate_matern_stationary"; + const string distance_matrix = "eg"; + const int dts = 8; + const int lts = 0; + const vector initial_theta = {1, 0.1, 0.5}; const vector estimated_theta = {0.9, 0.2, 0.5}; const vector lower_bound = {0.1, 0.1, 0.1}; const vector upper_bound = {5, 5, 5}; + vector> train_data = { + {0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440, + 0.652140077821011688, 0.806332494087129037, 0.553322652018005678, 0.800961318379491916, + 0.207324330510414295, 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, + 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, 0.877592126344701295}, + {0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537, + 0.168459601739528508, 0.105195696955825133, 0.396398870832379624, 0.296757457846952011, + 0.564507515068284116, 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, + 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, 0.942824444953078489}, + {-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, + 0.313503633252489700, -1.474410682226017677, 0.161705025505231914, 0.623389205185149065, + -1.341858445399783495, -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, + 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, 0.290822066007430102} + }; + vector> test_data = { + {0.193041886015106440, 0.330556191348134576}, + {0.103883421072709245, 0.135790035858701447} + }; + auto hardware = ExaGeoStatHardware(computation, cores_number, gpus_number); - SECTION("R METHODS") { -// auto exageostat_data = R_ExaGeoStatLoadData("univariate_matern_stationary", initial_theta, "eg", 16, 0, 8, 0, -// "2D", "", "", "", ""); -// vector estimated_theta = R_ExaGeoStatModelData("exact", "univariate_matern_stationary", "eg", -// lower_bound, upper_bound, 4, 10, 8, 0, "2D", 0, 500,exageostat_data); - - - vector> train_data = { - {0.181612878614480805, - 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, - 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, - 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, - 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, - 0.877592126344701295}, - {0.434683756771190977, - 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, - 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, - 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, - 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, - 0.942824444953078489}, - {/*-1.272336140360187606, -2.590699695867695773,*/ 0.512142584178685967, - -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, - 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, - -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, - 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, - 0.290822066007430102} - }; - vector> test_data = { - {0.193041886015106440, 0.330556191348134576}, - {0.103883421072709245, 0.135790035858701447} - }; - auto result = R_ExaGeoStatPredictData("univariate_matern_stationary", "eg", estimated_theta, 8, 0, "2D", train_data, test_data); - for(double i : result){ - cout << i << " "; + SECTION("Prediction Method") { + + // Since we're testing with the same x and y that we sent to the train data, then we expect the same output. + auto result = R_ExaGeoStatPredictData(kernel, distance_matrix, estimated_theta, dts, lts, dimension, train_data, + test_data); + for (int i = 0; i < result.size(); i++) { + REQUIRE((result[i] - train_data[2][i]) == Catch::Approx(0.0).margin(1e-6)); } -// delete exageostat_data; - } - SECTION("PREDICTION - Fisher"){ - - vector> train_data = { - {0.193041886015106440, 0.330556191348134576, 0.181612878614480805, - 0.370473792629892440, 0.652140077821011688, 0.806332494087129037, - 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, - 0.347951476310368490, 0.092042420080872822, 0.465445944914930965, - 0.528267338063630132, 0.974792095826657490, 0.552452887769893985, - 0.877592126344701295}, - {0.103883421072709245, 0.135790035858701447, 0.434683756771190977, - 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, - 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, - 0.627679865720607300, 0.928648813611047563, 0.958236057068741931, - 0.573571374074921758, 0.568657969024185528, 0.935835812924391552, - 0.942824444953078489}, - {-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, - -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, - 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, - -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, - 0.971213790000161170, 0.538973474182433021, -0.752828466476077041, - 0.290822066007430102} - }; - vector> test_data = { - {0.193041886015106440, 0.330556191348134576}, - {0.103883421072709245, 0.135790035858701447} + + // Now let's test with new values + test_data = { + {0.2, 0.3}, + {0.2, 0.2} }; - auto result = R_ExaGeoStatFisher("univariate_matern_stationary", "eg", estimated_theta, 8, 0, "2D", train_data, test_data); - for(double i : result){ - cout << i << " "; + result = R_ExaGeoStatPredictData(kernel, distance_matrix, estimated_theta, dts, lts, dimension, train_data, + test_data); + vector expected_output = {-1.04437, -1.63133}; + for (int i = 0; i < result.size(); i++) { + REQUIRE((result[i] - expected_output[i]) == Catch::Approx(0.0).margin(1e-5)); + } + + }SECTION("PREDICTION - Fisher") { + + auto result = R_ExaGeoStatFisher(kernel, distance_matrix, estimated_theta, dts, lts, dimension, train_data, + test_data); + + vector expected_output = {0.143552740378975224, 0.022642203822768981, 0.013749788660229965, + 0.022642203822769023, 0.143052423579629107, -0.371722296732991009, + 0.013749788660229986, -0.371722296732990953, 1.101996735797240667}; + + for (int i = 0; i < result.size(); i++) { + REQUIRE((result[i] - expected_output[i]) == Catch::Approx(0.0).margin(1e-6)); } } - SECTION("PREDICTION - MLOE-MMOM"){ - - vector> train_data = { - {0.092042420080872822, 0.193041886015106440, 0.330556191348134576, - 0.181612878614480805, 0.370473792629892440, 0.652140077821011688, - 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, - 0.465445944914930965, 0.528267338063630132, 0.974792095826657490, - 0.552452887769893985, 0.877592126344701295}, - {0.928648813611047563, 0.103883421072709245, 0.135790035858701447, 0.434683756771190977, - 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, - 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, - 0.627679865720607300, 0.958236057068741931, - 0.573571374074921758, 0.568657969024185528}, - {-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, - -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, - 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, - -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, - 0.971213790000161170, 0.538973474182433021} - }; - vector> test_data = { + SECTION("PREDICTION - MLOE-MMOM") { + test_data = { {0.347951, 0.62768}, {0.806332, 0.105196}, }; - auto result = R_ExaGeoStatMLOE_MMOM("univariate_matern_stationary", "eg", estimated_theta, initial_theta,8, 0, "2D", train_data, test_data); - for(double i : result){ - cout << i << " "; + auto result = R_ExaGeoStatMLOE_MMOM(kernel, distance_matrix, estimated_theta, initial_theta, dts, lts, + dimension, train_data, test_data); + vector expected_output = {0.0529825, -0.408526}; + for (int i = 0; i < result.size(); i++) { + REQUIRE((result[i] - expected_output[i]) == Catch::Approx(0.0).margin(1e-6)); + } + + result = R_ExaGeoStatMLOE_MMOM("univariate_matern_stationary", "eg", initial_theta, initial_theta, 8, 0, "2D", + train_data, test_data); + for (double i: result) { + REQUIRE((i - 0) == Catch::Approx(0.0).margin(1e-6)); } } - SECTION("PREDICTION - IDW"){ - - vector> train_data = { - {0.092042420080872822, 0.193041886015106440, 0.330556191348134576, - 0.181612878614480805, 0.370473792629892440, 0.652140077821011688, - 0.553322652018005678, 0.800961318379491916, 0.207324330510414295, - 0.465445944914930965, 0.528267338063630132, 0.974792095826657490, - 0.552452887769893985, 0.877592126344701295}, - {0.928648813611047563, 0.103883421072709245, 0.135790035858701447, 0.434683756771190977, - 0.400778210116731537, 0.168459601739528508, 0.105195696955825133, - 0.396398870832379624, 0.296757457846952011, 0.564507515068284116, - 0.627679865720607300, 0.958236057068741931, - 0.573571374074921758, 0.568657969024185528}, - {-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, - -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, - 0.161705025505231914, 0.623389205185149065, -1.341858445399783495, - -1.054282062428600009, -1.669383221392507943, 0.219170645803740793, - 0.971213790000161170, 0.538973474182433021} - }; - vector> test_data = { - {0.347951, 0.62768}, - {0.806332, 0.105196}, - }; + SECTION("PREDICTION - IDW") { + vector test_measurements = { -1.05428, -1.47441 }; - auto result = R_ExaGeoStatIDW("univariate_matern_stationary", "eg", estimated_theta, 8, 0, "2D", train_data, test_data, test_measurements); - for(double i : result){ - cout << i << " "; + auto result = R_ExaGeoStatIDW(kernel, distance_matrix, estimated_theta, dts, lts, dimension, train_data, + test_data, test_measurements); + vector expected_output = {0.447903, 0.100866, 0.79494}; + for (int i = 0; i < result.size(); i++) { + REQUIRE((result[i] - expected_output[i]) == Catch::Approx(0.0).margin(1e-6)); } } - } TEST_CASE("Test R/Rcpp adapters in C++") { + TEST_DATA_GENERATION(); TEST_ALL_R_METHODS(); } \ No newline at end of file diff --git a/tests/cpp-tests/api/TestExaGeoStatApi.cpp b/tests/cpp-tests/api/TestExaGeoStatApi.cpp index ff90f295..db707013 100644 --- a/tests/cpp-tests/api/TestExaGeoStatApi.cpp +++ b/tests/cpp-tests/api/TestExaGeoStatApi.cpp @@ -55,7 +55,7 @@ void TEST_GENERATE_DATA() { 0.623389, -1.341858, -1.054282, -1.669383, 0.219171, 0.971214, 0.538973, -0.752828, 0.290822}; auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc; + DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; double diff; @@ -107,7 +107,8 @@ void TEST_MODEL_DATA(Computation aComputation) { { // initialize ExaGeoStat Hardware. auto hardware = ExaGeoStatHardware(aComputation, 4, 0); // Or you could use configurations.GetComputation(). - std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>( + configurations.GetProblemSize(), configurations.GetDimension()); //initiating the matrix of the CHAMELEON Descriptor Z. @@ -135,7 +136,8 @@ void TEST_MODEL_DATA(Computation aComputation) { data->GetLocations()->SetLocationX(*location_x, N); data->GetLocations()->SetLocationY(*location_y, N); - double log_likelihood = exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(configurations, data, z_matrix); + double log_likelihood = exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(configurations, data, + z_matrix); REQUIRE((log_likelihood - expected) == Catch::Approx(0.0).margin(1e-6)); delete[] location_x; @@ -177,7 +179,8 @@ void TEST_PREDICTION() { Configurations::SetVerbosity(QUIET_MODE); auto hardware = ExaGeoStatHardware(EXACT_DENSE, configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); - std::unique_ptr> data = std::make_unique>(configurations.GetProblemSize(), configurations.GetDimension()); + std::unique_ptr> data = std::make_unique>( + configurations.GetProblemSize(), configurations.GetDimension()); auto *z_matrix = new double[N]{-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520, 0.313503633252489700, -1.474410682226017677, @@ -241,7 +244,7 @@ void TEST_PREDICTION() { configurations.SetIsIDW(true); configurations.SetIsFisher(true); configurations.SetIsMLOEMMOM(true); - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(configurations,data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(configurations, data); exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(configurations, data); exageostat::api::ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); } @@ -254,6 +257,8 @@ TEST_CASE("ExaGeoStat API tests") { TEST_GENERATE_DATA(); TEST_MODEL_DATA(EXACT_DENSE); TEST_MODEL_DATA(DIAGONAL_APPROX); +#ifdef USE_HICMA TEST_MODEL_DATA(TILE_LOW_RANK); +#endif TEST_PREDICTION(); } \ No newline at end of file diff --git a/tests/cpp-tests/configurations/TestConfigurations.cpp b/tests/cpp-tests/configurations/TestConfigurations.cpp index d8b2e9e8..76a2e715 100644 --- a/tests/cpp-tests/configurations/TestConfigurations.cpp +++ b/tests/cpp-tests/configurations/TestConfigurations.cpp @@ -25,27 +25,27 @@ using namespace std; using namespace exageostat::common; using namespace exageostat::configurations; -void TEST_ARGUMENT_INITIALIZATION(){ +void TEST_ARGUMENT_INITIALIZATION() { const int argc = 17; - char* argv[] = { - const_cast("program_name"), - const_cast("--N=16"), - const_cast("--dts=8"), - const_cast("--kernel=univariate_matern_stationary"), - const_cast("--computation=exact"), - const_cast("--precision=double"), - const_cast("--initial_theta=1:0.1:0.5"), - const_cast("--ub=5:5:5"), - const_cast("--lb=0.1:0.1:0.1"), - const_cast("--max_mle_iterations=5"), - const_cast("--tolerance=4"), - const_cast("--ZMiss=6"), - const_cast("--mspe"), - const_cast("--idw"), - const_cast("--mloe-mmom"), - const_cast("--fisher"), - const_cast("--data_path=./dummy-path") + char *argv[] = { + const_cast("program_name"), + const_cast("--N=16"), + const_cast("--dts=8"), + const_cast("--kernel=univariate_matern_stationary"), + const_cast("--computation=exact"), + const_cast("--precision=double"), + const_cast("--initial_theta=1:0.1:0.5"), + const_cast("--ub=5:5:5"), + const_cast("--lb=0.1:0.1:0.1"), + const_cast("--max_mle_iterations=5"), + const_cast("--tolerance=4"), + const_cast("--ZMiss=6"), + const_cast("--mspe"), + const_cast("--idw"), + const_cast("--mloe-mmom"), + const_cast("--fisher"), + const_cast("--data_path=./dummy-path") }; Configurations configurations; @@ -93,6 +93,7 @@ void TEST_ARGUMENT_INITIALIZATION(){ REQUIRE(configurations.GetUnknownObservationsNb() == 6); } + void TEST_SYNTHETIC_CONFIGURATIONS() { Configurations synthetic_data_configurations; diff --git a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp index b67b2218..4fea5441 100644 --- a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp @@ -48,11 +48,12 @@ void TEST_CSV_P_1() { vector initial_theta{1, 0.1, 0.5}; configurations.SetInitialTheta(initial_theta); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), configurations.GetTimeSlot()); auto hardware = ExaGeoStatHardware(configurations.GetComputation(), - configurations.GetCoresNumber(), - configurations.GetGPUsNumbers()); + configurations.GetCoresNumber(), + configurations.GetGPUsNumbers()); //creating locations x and y. auto *location_x = new double[N]{0.193041886015106440, 0.330556191348134576, 0.181612878614480805, @@ -91,10 +92,7 @@ void TEST_CSV_P_1() { CSVLoader::GetInstance()->WriteData(*measurements_matrix, N, p, write_path, locations); auto data = csv_reader->CreateData(configurations, *pKernel); - auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data->GetDescriptorData()->GetDescriptor( - CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc); + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z); auto data_loc_x = data->GetLocations()->GetLocationX(); auto data_loc_y = data->GetLocations()->GetLocationY(); @@ -124,10 +122,7 @@ void TEST_CSV_P_1() { CSVLoader::GetInstance()->WriteData(*measurements_matrix, N, p, write_path, locations); auto data = csv_reader->CreateData(configurations, *pKernel); - auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data->GetDescriptorData()->GetDescriptor( - CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc); + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z); auto data_loc_x = data->GetLocations()->GetLocationX(); auto data_loc_y = data->GetLocations()->GetLocationY(); auto data_loc_z = data->GetLocations()->GetLocationZ(); @@ -160,10 +155,7 @@ void TEST_CSV_P_1() { CSVLoader::GetInstance()->WriteData(*measurements_matrix, N, p, write_path, locations); auto data = csv_reader->CreateData(configurations, *pKernel); - auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data->GetDescriptorData()->GetDescriptor( - CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc); + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z); auto data_loc_x = data->GetLocations()->GetLocationX(); auto data_loc_y = data->GetLocations()->GetLocationY(); auto data_loc_time = data->GetLocations()->GetLocationZ(); @@ -198,12 +190,13 @@ void TEST_CSV_P_2() { configurations.SetComputation(exageostat::common::EXACT_DENSE); configurations.SetDataPath(read_path); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), configurations.GetTimeSlot()); int p = pKernel->GetVariablesNumber(); auto hardware = ExaGeoStatHardware(configurations.GetComputation(), - configurations.GetCoresNumber(), - configurations.GetGPUsNumbers()); + configurations.GetCoresNumber(), + configurations.GetGPUsNumbers()); //creating locations x and y. auto *location_x = new double[N]{0.193041886015106440, 0.330556191348134576, 0.181612878614480805, @@ -251,13 +244,10 @@ void TEST_CSV_P_2() { locations.SetLocationY(*location_y, N); CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, - locations); + locations); auto data = csv_reader->CreateData(configurations, *pKernel); - auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data->GetDescriptorData()->GetDescriptor( - CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc); + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z); auto data_loc_x = data->GetLocations()->GetLocationX(); auto data_loc_y = data->GetLocations()->GetLocationY(); @@ -288,13 +278,10 @@ void TEST_CSV_P_2() { locations.SetLocationZ(*location_z, N); CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, - locations); + locations); auto data = csv_reader->CreateData(configurations, *pKernel); - auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data->GetDescriptorData()->GetDescriptor( - CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc); + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z); auto data_loc_x = data->GetLocations()->GetLocationX(); auto data_loc_y = data->GetLocations()->GetLocationY(); auto data_loc_z = data->GetLocations()->GetLocationZ(); @@ -328,13 +315,10 @@ void TEST_CSV_P_2() { locations.SetLocationZ(*location_time, N); CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, - locations); + locations); auto data = csv_reader->CreateData(configurations, *pKernel); - auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data->GetDescriptorData()->GetDescriptor( - CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc); + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z); auto data_loc_x = data->GetLocations()->GetLocationX(); auto data_loc_y = data->GetLocations()->GetLocationY(); auto data_loc_time = data->GetLocations()->GetLocationZ(); @@ -371,12 +355,13 @@ void TEST_CSV_P_3() { vector initial_theta{1, 1, 1, 0.1, 0.5, 1, 1.5, 0.1, 0.1, 0}; configurations.SetInitialTheta(initial_theta); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(configurations.GetKernelName(), configurations.GetTimeSlot()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), configurations.GetTimeSlot()); int p = pKernel->GetVariablesNumber(); auto hardware = ExaGeoStatHardware(configurations.GetComputation(), - configurations.GetCoresNumber(), - configurations.GetGPUsNumbers()); + configurations.GetCoresNumber(), + configurations.GetGPUsNumbers()); //creating locations x and y. auto *location_x = new double[N]{0.193041886015106440, 0.330556191348134576, 0.181612878614480805, @@ -421,13 +406,10 @@ void TEST_CSV_P_3() { locations.SetLocationY(*location_y, N); CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, - locations); + locations); auto data = csv_reader->CreateData(configurations, *pKernel); - auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data->GetDescriptorData()->GetDescriptor( - CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc); + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z); auto data_loc_x = data->GetLocations()->GetLocationX(); auto data_loc_y = data->GetLocations()->GetLocationY(); @@ -456,13 +438,10 @@ void TEST_CSV_P_3() { locations.SetLocationZ(*location_z, N); CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, - locations); + locations); auto data = csv_reader->CreateData(configurations, *pKernel); - auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data->GetDescriptorData()->GetDescriptor( - CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc); + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z); auto data_loc_x = data->GetLocations()->GetLocationX(); auto data_loc_y = data->GetLocations()->GetLocationY(); auto data_loc_z = data->GetLocations()->GetLocationZ(); @@ -494,13 +473,10 @@ void TEST_CSV_P_3() { locations.SetLocationZ(*location_time, N); CSVLoader::GetInstance()->WriteData(*measurements_matrix, N * p, p, write_path, - locations); + locations); auto data = csv_reader->CreateData(configurations, *pKernel); - auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, - data->GetDescriptorData()->GetDescriptor( - CHAMELEON_DESCRIPTOR, - DESCRIPTOR_Z).chameleon_desc); + auto z_desc_mat = data->GetDescriptorData()->GetDescriptorMatrix(CHAMELEON_DESCRIPTOR, DESCRIPTOR_Z); auto data_loc_x = data->GetLocations()->GetLocationX(); auto data_loc_y = data->GetLocations()->GetLocationY(); auto data_loc_time = data->GetLocations()->GetLocationZ(); diff --git a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp index 75d5e088..a8c540f5 100644 --- a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp @@ -194,12 +194,13 @@ void TEST_GENERATE_LOCATIONS() { synthetic_data_configurations.SetComputation(exageostat::common::EXACT_DENSE); vector initial_theta{1, 0.1, 0.5}; synthetic_data_configurations.SetInitialTheta(initial_theta); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(synthetic_data_configurations.GetKernelName(), synthetic_data_configurations.GetTimeSlot()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + synthetic_data_configurations.GetKernelName(), synthetic_data_configurations.GetTimeSlot()); auto hardware = ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), - synthetic_data_configurations.GetCoresNumber(), - synthetic_data_configurations.GetGPUsNumbers()); + synthetic_data_configurations.GetCoresNumber(), + synthetic_data_configurations.GetGPUsNumbers()); SECTION("2D Generation") { unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( @@ -216,8 +217,7 @@ void TEST_GENERATE_LOCATIONS() { REQUIRE(x[i] != 0); REQUIRE(y[i] != 0); } - } - SECTION("3D Generation") + }SECTION("3D Generation") { synthetic_data_configurations.SetDimension(Dimension3D); unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( @@ -233,9 +233,8 @@ void TEST_GENERATE_LOCATIONS() { REQUIRE(y[i] != 0); REQUIRE(z[i] != 0); } - - } - SECTION("ST Generation") + + }SECTION("ST Generation") { synthetic_data_configurations.SetDimension(DimensionST); synthetic_data_configurations.SetTimeSlot(2); @@ -252,7 +251,7 @@ void TEST_GENERATE_LOCATIONS() { REQUIRE(y[i] != 0.0); REQUIRE(z[i] != 0.0); } - + } delete pKernel; @@ -299,10 +298,11 @@ void TEST_GENERATION() { synthetic_data_configurations.SetKernelName("UnivariateMaternStationary"); synthetic_data_configurations.SetComputation(exageostat::common::EXACT_DENSE); auto hardware = ExaGeoStatHardware(synthetic_data_configurations.GetComputation(), - synthetic_data_configurations.GetCoresNumber(), - synthetic_data_configurations.GetGPUsNumbers()); + synthetic_data_configurations.GetCoresNumber(), + synthetic_data_configurations.GetGPUsNumbers()); - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(synthetic_data_configurations.GetKernelName(), synthetic_data_configurations.GetTimeSlot()); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + synthetic_data_configurations.GetKernelName(), synthetic_data_configurations.GetTimeSlot()); unique_ptr> synthetic_generator = DataGenerator::CreateGenerator( synthetic_data_configurations); diff --git a/tests/cpp-tests/data-units/TestDescriptorData.cpp b/tests/cpp-tests/data-units/TestDescriptorData.cpp index 0615fc21..3348d5ac 100644 --- a/tests/cpp-tests/data-units/TestDescriptorData.cpp +++ b/tests/cpp-tests/data-units/TestDescriptorData.cpp @@ -23,7 +23,7 @@ using namespace exageostat::common; using namespace exageostat::dataunits; using namespace exageostat::configurations; -void TEST_CHAM_TO_HICMA_CONV(){ +void TEST_CHAM_TO_HICMA_CONV() { // Initialize Configuration Configurations synthetic_data_configurations; @@ -43,22 +43,22 @@ void TEST_CHAM_TO_HICMA_CONV(){ // Verify common attributes are of same value REQUIRE(CHAM_descriptorC->m == HICMA_descriptor->m); - REQUIRE(CHAM_descriptorC->n == HICMA_descriptor->n); - REQUIRE(CHAM_descriptorC->mb == HICMA_descriptor->mb); - REQUIRE(CHAM_descriptorC->nb == HICMA_descriptor->nb); - REQUIRE(CHAM_descriptorC->bsiz == HICMA_descriptor->bsiz); - REQUIRE(CHAM_descriptorC->i == HICMA_descriptor->i); - REQUIRE(CHAM_descriptorC->j == HICMA_descriptor->j); - REQUIRE(CHAM_descriptorC->mt == HICMA_descriptor->mt); - REQUIRE(CHAM_descriptorC->nt == HICMA_descriptor->nt); - REQUIRE(CHAM_descriptorC->lm == HICMA_descriptor->lm); - REQUIRE(CHAM_descriptorC->ln == HICMA_descriptor->ln); - REQUIRE(CHAM_descriptorC->p == HICMA_descriptor->p); - REQUIRE(CHAM_descriptorC->q == HICMA_descriptor->q); + REQUIRE(CHAM_descriptorC->n == HICMA_descriptor->n); + REQUIRE(CHAM_descriptorC->mb == HICMA_descriptor->mb); + REQUIRE(CHAM_descriptorC->nb == HICMA_descriptor->nb); + REQUIRE(CHAM_descriptorC->bsiz == HICMA_descriptor->bsiz); + REQUIRE(CHAM_descriptorC->i == HICMA_descriptor->i); + REQUIRE(CHAM_descriptorC->j == HICMA_descriptor->j); + REQUIRE(CHAM_descriptorC->mt == HICMA_descriptor->mt); + REQUIRE(CHAM_descriptorC->nt == HICMA_descriptor->nt); + REQUIRE(CHAM_descriptorC->lm == HICMA_descriptor->lm); + REQUIRE(CHAM_descriptorC->ln == HICMA_descriptor->ln); + REQUIRE(CHAM_descriptorC->p == HICMA_descriptor->p); + REQUIRE(CHAM_descriptorC->q == HICMA_descriptor->q); delete data; } -TEST_CASE(" CHAMELEON To HICMA Converter"){ +TEST_CASE(" CHAMELEON To HICMA Converter") { TEST_CHAM_TO_HICMA_CONV(); } diff --git a/tests/cpp-tests/hardware/CMakeLists.txt b/tests/cpp-tests/hardware/CMakeLists.txt index 370d53d7..a2812a74 100644 --- a/tests/cpp-tests/hardware/CMakeLists.txt +++ b/tests/cpp-tests/hardware/CMakeLists.txt @@ -11,4 +11,4 @@ set(EXAGEOSTAT_TESTFILES ${CMAKE_CURRENT_SOURCE_DIR}/TestExaGeoStatHardware.cpp ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE -) + ) diff --git a/tests/cpp-tests/helpers/CMakeLists.txt b/tests/cpp-tests/helpers/CMakeLists.txt index 4f093909..cea80b5c 100644 --- a/tests/cpp-tests/helpers/CMakeLists.txt +++ b/tests/cpp-tests/helpers/CMakeLists.txt @@ -14,4 +14,4 @@ set(EXAGEOSTAT_TESTFILES ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE -) + ) diff --git a/tests/cpp-tests/helpers/TestDiskWriter.cpp b/tests/cpp-tests/helpers/TestDiskWriter.cpp index 705aec50..85884430 100644 --- a/tests/cpp-tests/helpers/TestDiskWriter.cpp +++ b/tests/cpp-tests/helpers/TestDiskWriter.cpp @@ -65,7 +65,8 @@ void TEST_3D_VECTORS_WRITING() { std::ostringstream oss; oss << std::setprecision(15) << locations.GetLocationX()[i] << ',' - << locations.GetLocationY()[i] << ',' << locations.GetLocationZ()[i] << ","<< std::setprecision(15) << measurements_matrix[i] ; + << locations.GetLocationY()[i] << ',' << locations.GetLocationZ()[i] << "," << std::setprecision(15) + << measurements_matrix[i]; std::string expectedLine = oss.str(); REQUIRE(line == expectedLine); @@ -76,6 +77,7 @@ void TEST_3D_VECTORS_WRITING() { delete[] location_z; delete[] measurements_matrix; } -TEST_CASE("Disk Writer Tests"){ + +TEST_CASE("Disk Writer Tests") { TEST_3D_VECTORS_WRITING(); } diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp index 19aca623..c03d91af 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp @@ -51,7 +51,7 @@ void TEST_KERNEL_GENERATION_BivariateMaternFlexible() { std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; + exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output double expected_output_data[] = {-0.696887, -2.069051, -0.417382, -1.001165, -0.235721, -1.726871, -0.285059, diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp index 4bfabee3..14fe5c84 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp @@ -50,9 +50,9 @@ void TEST_KERNEL_GENERATION_BivariateMaternParsimonious() { int seed = 0; srand(seed); std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations,data); + exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; + exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output double expected_output_data[] = {-1.272336, -2.466950, 0.294719, -0.605327, 0.386028, -1.598090, 0.278897, diff --git a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp index 845e8c95..3cde4758 100644 --- a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp @@ -50,7 +50,7 @@ void TEST_KERNEL_GENERATION_TrivariateMaternParsimonious() { std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; + exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output double expected_output_data[] = { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp index 5cf79781..25b058f3 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp @@ -54,7 +54,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; + exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output double expected_output_data[] = { @@ -64,7 +64,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNonGaussian() { }; for (size_t i = 0; i < N; i++) { - double diff = A[i] - expected_output_data[i]/2; + double diff = A[i] - expected_output_data[i] / 2; REQUIRE(diff == Catch::Approx(0.0).margin(1e-6)); } } diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp index 31376d7d..5341f3c6 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp @@ -50,7 +50,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternNuggetsStationary() { std::unique_ptr> data; exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; + exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output double expected_output_data[] = { diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp index d2789b0b..d9c25231 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp @@ -52,7 +52,7 @@ void TEST_KERNEL_GENERATION_UnivariateMaternStationary() { exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); auto *CHAM_descriptorZ = data->GetDescriptorData()->GetDescriptor(exageostat::common::CHAMELEON_DESCRIPTOR, - exageostat::common::DESCRIPTOR_Z).chameleon_desc; + exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output double expected_output_data[] = {-1.272336, -2.475473, 0.545850, -0.120985, 0.242569, -1.544215, 0.098647, diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp index b618a308..7f9bf8a8 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp @@ -55,8 +55,8 @@ void TEST_KERNEL_GENERATION_UnivariatePowExpStationary() { exageostat::common::DESCRIPTOR_Z).chameleon_desc; auto *A = (double *) CHAM_descriptorZ->mat; // Define the expected output - double expected_output_data[] = { -1.272336, -2.362813, 0.616384, -0.072468, 0.401498, -1.559690, 0.211848, - 0.776627, -1.524810}; + double expected_output_data[] = {-1.272336, -2.362813, 0.616384, -0.072468, 0.401498, -1.559690, 0.211848, + 0.776627, -1.524810}; for (size_t i = 0; i < N; i++) { double diff = A[i] - expected_output_data[i]; diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp index 3bf617cc..efe3fd4f 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp @@ -100,14 +100,14 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { auto *HICMA_descriptorZ = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_Z).hicma_desc; auto *HICMA_descriptorZcpy = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, - DESCRIPTOR_Z_COPY).hicma_desc; + DESCRIPTOR_Z_COPY).hicma_desc; auto *HICMA_descriptorDeterminant = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, - DESCRIPTOR_DETERMINANT).hicma_desc; + DESCRIPTOR_DETERMINANT).hicma_desc; auto *HICMA_descriptorCD = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, DESCRIPTOR_CD).hicma_desc; auto *HICMA_descriptorCUV = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, - DESCRIPTOR_CUV).hicma_desc; + DESCRIPTOR_CUV).hicma_desc; auto *HICMA_descriptorCrk = data->GetDescriptorData()->GetDescriptor(HICMA_DESCRIPTOR, - DESCRIPTOR_CRK).hicma_desc; + DESCRIPTOR_CRK).hicma_desc; // Descriptor CD. REQUIRE(HICMA_descriptorCD->m == N); diff --git a/tests/cpp-tests/prediction/CMakeLists.txt b/tests/cpp-tests/prediction/CMakeLists.txt index 9a1d4bec..aa7af831 100644 --- a/tests/cpp-tests/prediction/CMakeLists.txt +++ b/tests/cpp-tests/prediction/CMakeLists.txt @@ -14,5 +14,5 @@ set(EXAGEOSTAT_TESTFILES ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE -) + ) diff --git a/tests/cpp-tests/prediction/TestPrediction.cpp b/tests/cpp-tests/prediction/TestPrediction.cpp index 31bce777..dbafbbb1 100644 --- a/tests/cpp-tests/prediction/TestPrediction.cpp +++ b/tests/cpp-tests/prediction/TestPrediction.cpp @@ -93,10 +93,9 @@ void TEST_PREDICTION_MISSING_DATA() { configurations.InitializeDataPredictionArguments(); Prediction::PredictMissingData(data, configurations, z_matrix, *pKernel); - REQUIRE(Results::GetInstance()->GetMSPEError()== Catch::Approx(0.552448)); + REQUIRE(Results::GetInstance()->GetMSPEError() == Catch::Approx(0.552448)); delete pKernel; - } - SECTION("Test Prediction - IDW ") { + }SECTION("Test Prediction - IDW ") { configurations.SetIsMLOEMMOM(false); configurations.SetIsFisher(false); configurations.SetIsMSPE(false); @@ -104,7 +103,7 @@ void TEST_PREDICTION_MISSING_DATA() { vector estimated_theta{0.9, 0.09, 0.4}; configurations.SetEstimatedTheta(estimated_theta); - std::vector idw_error={ 1.18856255, 1.25725881, 1.11986628 }; + std::vector idw_error = {1.18856255, 1.25725881, 1.11986628}; // Register and create a kernel object exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( @@ -113,12 +112,11 @@ void TEST_PREDICTION_MISSING_DATA() { // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); Prediction::PredictMissingData(data, configurations, z_matrix, *pKernel); - for(int i =0;i<3;i++){ + for (int i = 0; i < 3; i++) { REQUIRE(Results::GetInstance()->GetIDWError()[i] == Catch::Approx(idw_error[i])); } delete pKernel; - } - SECTION("Test Prediction - MLOE_MMOM ") { + }SECTION("Test Prediction - MLOE_MMOM ") { configurations.SetIsMSPE(false); configurations.SetIsIDW(false); configurations.SetIsMLOEMMOM(true); @@ -136,10 +134,21 @@ void TEST_PREDICTION_MISSING_DATA() { Prediction::PredictMissingData(data, configurations, z_matrix, *pKernel); REQUIRE(Results::GetInstance()->GetMLOE() == Catch::Approx(0.004467).margin(0.001)); REQUIRE(Results::GetInstance()->GetMMOM() == Catch::Approx(-0.0812376).margin(0.001)); + delete pKernel; + }SECTION("Test Prediction - MLOE_MMOM with equal estimated theta") { + configurations.SetIsMSPE(false); + configurations.SetIsIDW(false); + configurations.SetIsMLOEMMOM(true); + configurations.SetIsFisher(false); + vector new_estimated_theta{1, 0.1, 0.5}; + configurations.SetEstimatedTheta(new_estimated_theta); + + // Register and create a kernel object + exageostat::kernels::Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create( + configurations.GetKernelName(), + configurations.GetTimeSlot()); - vector new_estimated_theta1{1, 0.1, 0.5}; - configurations.SetEstimatedTheta(new_estimated_theta1); // Add the data prediction arguments. configurations.InitializeDataPredictionArguments(); Prediction::PredictMissingData(data, configurations, z_matrix, *pKernel); @@ -164,14 +173,14 @@ void TEST_PREDICTION_MISSING_DATA() { configurations.InitializeDataPredictionArguments(); Prediction::PredictMissingData(data, configurations, z_matrix, *pKernel); - vector required_fisher = {0.1045891821, 0.0005116817, 0.0409307011, 0.0005116817, 0.1873553354, -1.3659618079, 0.0409307011, -1.3659618079, 10.5564826575}; - for(int i = 0; i < Results::GetInstance()->GetFisherMatrix().size(); i++){ + vector required_fisher = {0.1045891821, 0.0005116817, 0.0409307011, 0.0005116817, 0.1873553354, + -1.3659618079, 0.0409307011, -1.3659618079, 10.5564826575}; + for (int i = 0; i < Results::GetInstance()->GetFisherMatrix().size(); i++) { double diff = required_fisher[i] - Results::GetInstance()->GetFisherMatrix()[i]; REQUIRE(diff == Catch::Approx(0.0).margin(1e-6)); } delete pKernel; - } - SECTION("Test Prediction - Exception"){ + }SECTION("Test Prediction - Exception") { vector new_estimated_theta{-1, -1, -1}; configurations.SetEstimatedTheta(new_estimated_theta); Prediction predictor; diff --git a/tests/cpp-tests/results/CMakeLists.txt b/tests/cpp-tests/results/CMakeLists.txt index 4ef3c229..2f9216be 100644 --- a/tests/cpp-tests/results/CMakeLists.txt +++ b/tests/cpp-tests/results/CMakeLists.txt @@ -11,4 +11,4 @@ set(EXAGEOSTAT_TESTFILES ${CMAKE_CURRENT_SOURCE_DIR}/TestResults.cpp ${EXAGEOSTAT_TESTFILES} PARENT_SCOPE -) + ) diff --git a/tests/cpp-tests/results/TestResults.cpp b/tests/cpp-tests/results/TestResults.cpp index acacea48..c77ae811 100644 --- a/tests/cpp-tests/results/TestResults.cpp +++ b/tests/cpp-tests/results/TestResults.cpp @@ -25,9 +25,9 @@ void TEST_SINGLETON_RESULTS() { } void TEST_SETTERS_AND_GETTERS() { - + auto results_instacne = Results::GetInstance(); - + SECTION("Total Modeling Execution Time Setter/Getter") { results_instacne->SetTotalModelingExecutionTime(1.0); REQUIRE(results_instacne->GetTotalModelingExecutionTime() == 1.0); diff --git a/tests/heavy-tests/ExamplesTests.cpp b/tests/heavy-tests/ExamplesTests.cpp index 7566ca7a..843965c0 100644 --- a/tests/heavy-tests/ExamplesTests.cpp +++ b/tests/heavy-tests/ExamplesTests.cpp @@ -24,73 +24,115 @@ using namespace exageostat::api; TEST_CASE("EXAMPLES") { - // Specify the examples path - path currentPath = current_path().parent_path().parent_path() / "examples"; - - SECTION("Configuration"){ - path configurations_example = currentPath / "configurations/Example_Configurations_Setup "; - string arguments_string = "--N=10 --dts=8 --kernel=univariate_matern_stationary"; - cout << "Running Configurations example with arguments: " + arguments_string << endl << flush; - - std::string fullCommand = std::string(configurations_example) + " " + arguments_string; - if (std::system(fullCommand.c_str())) { - throw runtime_error("This test failed " + fullCommand); - } - } - SECTION("Synthetic Data Generation"){ - path synthetic_data_example = currentPath / "data-generators/Example_Synthetic_Data_Generation"; - string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --dimension=2d --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1"; - cout << "Running synthetic locations example with arguments: " + arguments_string << endl << flush; - - std::string fullCommand = std::string(synthetic_data_example) + " " + arguments_string; - if (std::system(fullCommand.c_str())) { - throw runtime_error("This test failed " + fullCommand); - } - } - currentPath = currentPath / "end-to-end"; - SECTION("Data Generation Module"){ - path data_generation_example = currentPath / "Example_Data_Generation"; - string arguments_string = "--N=32 --dts=8 --kernel=univariate_matern_stationary --dimension=3D --computation=diagonal_approx --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1"; - cout << "Running Data Generation Module example with arguments: " + arguments_string << endl << flush; - - std::string fullCommand = std::string(data_generation_example) + " " + arguments_string; - if (std::system(fullCommand.c_str())) { - throw runtime_error("This test failed " + fullCommand); - } - } - SECTION("Data Modeling Module"){ - path data_modeling_example = currentPath / "Example_Data_Modeling"; +// Specify the examples path +path currentPath = current_path().parent_path().parent_path() / "examples"; + +SECTION("Configuration"){ +path configurations_example = currentPath / "configurations/Example_Configurations_Setup "; +string arguments_string = "--N=10 --dts=8 --kernel=univariate_matern_stationary"; +cout << "Running Configurations example with arguments: " + arguments_string << endl << +flush; + +std::string fullCommand = std::string(configurations_example) + " " + arguments_string; +if ( +std::system(fullCommand +. + +c_str() + +)) { +throw runtime_error("This test failed " + fullCommand); +} +} +SECTION("Synthetic Data Generation"){ +path synthetic_data_example = currentPath / "data-generators/Example_Synthetic_Data_Generation"; +string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --dimension=2d --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1"; +cout << "Running synthetic locations example with arguments: " + arguments_string << endl << +flush; + +std::string fullCommand = std::string(synthetic_data_example) + " " + arguments_string; +if ( +std::system(fullCommand +. + +c_str() + +)) { +throw runtime_error("This test failed " + fullCommand); +} +} +currentPath = currentPath / "end-to-end"; +SECTION("Data Generation Module"){ +path data_generation_example = currentPath / "Example_Data_Generation"; +string arguments_string = "--N=32 --dts=8 --kernel=univariate_matern_stationary --dimension=3D --computation=diagonal_approx --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1"; +cout << "Running Data Generation Module example with arguments: " + arguments_string << endl << +flush; + +std::string fullCommand = std::string(data_generation_example) + " " + arguments_string; +if ( +std::system(fullCommand +. + +c_str() + +)) { +throw runtime_error("This test failed " + fullCommand); +} +} +SECTION("Data Modeling Module"){ +path data_modeling_example = currentPath / "Example_Data_Modeling"; #ifdef USE_HICMA - string arguments_string = "--N=16 --dts=8 --lts=8 --kernel=univariate_matern_stationary --dimension=2D --computation=tlr --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --max_mle_iterations=3 --tolerance=10 --max_rank=500"; +string arguments_string = "--N=16 --dts=8 --lts=8 --kernel=univariate_matern_stationary --dimension=2D --computation=tlr --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --max_mle_iterations=3 --tolerance=10 --max_rank=500"; #else - string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --max_mle_iterations=3 --tolerance=10"; +string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --max_mle_iterations=3 --tolerance=10"; #endif - cout << "Running Data Modeling example with arguments: " + arguments_string << endl << flush; - std::string fullCommand = std::string(data_modeling_example) + " " + arguments_string; - if (std::system(fullCommand.c_str())) { - throw runtime_error("This test failed " + fullCommand); - } - } - SECTION("Data Prediction Module"){ - path data_prediction_example = currentPath / "Example_Data_Prediction"; - - string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --Zmiss=10 --mspe --fisher --mloe_mmom --etheta=1.1:0.1:0.5"; - cout << "Running Data Prediction example with arguments: " + arguments_string << endl << flush; - - std::string fullCommand = std::string(data_prediction_example) + " " + arguments_string; - if (std::system(fullCommand.c_str())) { - throw runtime_error("This test failed " + fullCommand); - } - } - SECTION("Data Generation and Modeling"){ - path data_generation_modeling_example = currentPath / "Example_Data_Generation_and_Modeling"; - - string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --max_mle_iterations=2 --tolerance=4 --etheta=1.1:?:0.5"; - cout << "Running Data data generation and modeling example with arguments: " + arguments_string << endl << flush; - - std::string fullCommand = std::string(data_generation_modeling_example) + " " + arguments_string; - if (std::system(fullCommand.c_str())) { - throw runtime_error("This test failed " + fullCommand); - } - } +cout << "Running Data Modeling example with arguments: " + arguments_string << endl << +flush; +std::string fullCommand = std::string(data_modeling_example) + " " + arguments_string; +if ( +std::system(fullCommand +. + +c_str() + +)) { +throw runtime_error("This test failed " + fullCommand); +} +} +SECTION("Data Prediction Module"){ +path data_prediction_example = currentPath / "Example_Data_Prediction"; + +string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --Zmiss=10 --mspe --fisher --mloe_mmom --etheta=1.1:0.1:0.5"; +cout << "Running Data Prediction example with arguments: " + arguments_string << endl << +flush; + +std::string fullCommand = std::string(data_prediction_example) + " " + arguments_string; +if ( +std::system(fullCommand +. + +c_str() + +)) { +throw runtime_error("This test failed " + fullCommand); +} +} +SECTION("Data Generation and Modeling"){ +path data_generation_modeling_example = currentPath / "Example_Data_Generation_and_Modeling"; + +string arguments_string = "--N=16 --dts=8 --kernel=univariate_matern_stationary --computation=exact --itheta=1:0.1:0.5 --ub=5:5:5 --lb=0.1:0.1:0.1 --max_mle_iterations=2 --tolerance=4 --etheta=1.1:?:0.5"; +cout << "Running Data data generation and modeling example with arguments: " + arguments_string << endl << +flush; + +std::string fullCommand = std::string(data_generation_modeling_example) + " " + arguments_string; +if ( +std::system(fullCommand +. + +c_str() + +)) { +throw runtime_error("This test failed " + fullCommand); +} +} } diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index 49c2d61e..2cd0563c 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -14,6 +14,7 @@ #include #include #include + #ifdef USE_CUDA #include #endif @@ -29,8 +30,8 @@ using namespace exageostat::dataunits; using namespace exageostat::api; using namespace exageostat::kernels; -void GenerateCommandLineArguments(const string &aKernelName, const string &aComputation, vector &aDimensions, - const string &aPrecision, vector &arguments_vector, +void GenerateCommandLineArguments(const string &aKernelName, const string &aComputation, vector &aDimensions, + const string &aPrecision, vector &arguments_vector, const string &aDistanceType) { // Search for the substring "TimeSpace" in the kernel name, As ST dimension only works with kernels having Spacetime in their name. @@ -86,7 +87,7 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp uniform_int_distribution zMiss_distribution(2, min(N_value / 2, 100)); // Create a kernel object to get the number of parameters for each kernel. - Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(aKernelName, 1); + Kernel *pKernel = exageostat::plugins::PluginRegistry < Kernel < double >> ::Create(aKernelName, 1); int param_number = pKernel->GetParametersNumbers(); delete pKernel; @@ -166,87 +167,120 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp TEST_CASE("END-TO-END") { - // Add all the possible combination of code. - vector kernels_vector = { - "BivariateMaternFlexible", - "BivariateMaternParsimonious", - "BivariateSpacetimeMaternStationary", - "TrivariateMaternParsimonious", - "UnivariateExpNonGaussian", - "UnivariateMaternNonGaussian", - "UnivariateMaternNuggetsStationary", - "UnivariateMaternStationary", - "UnivariateSpacetimeMaternStationary" - }; - - vector computation_vector = {"exact", "diag_approx"}; +// Add all the possible combination of code. +vector kernels_vector = { + "BivariateMaternFlexible", + "BivariateMaternParsimonious", + "BivariateSpacetimeMaternStationary", + "TrivariateMaternParsimonious", + "UnivariateExpNonGaussian", + "UnivariateMaternNonGaussian", + "UnivariateMaternNuggetsStationary", + "UnivariateMaternStationary", + "UnivariateSpacetimeMaternStationary" +}; + +vector computation_vector = {"exact", "diag_approx"}; #ifdef USE_HICMA - computation_vector.emplace_back("tlr"); +computation_vector.emplace_back("tlr"); #endif - vector dimensions_vector; - vector precision_vector = {"single", "double"}; - vector distance_vector = {"eg", "gcd"}; - - size_t combination_number = 1; - size_t number_of_iterations = 1; - for (int i = 0; i < number_of_iterations; i++) { - cout << "**** END-TO_END TESTS -- ITERATION NUMBER (" << i + 1 << ") ****" << endl; - // Generate combinations. - for (const auto ¤t_kernel: kernels_vector) { - for (const auto &computation: computation_vector) { - for (const auto &precision: precision_vector) { - for (const auto &distance_type: distance_vector) { - vector arguments_vector; - // This helper function will fill the arguments vector with the software arguments commands - GenerateCommandLineArguments(current_kernel, computation, dimensions_vector, precision, - arguments_vector, distance_type); - // Generate combination of dimensions. - for (const auto &dimension: dimensions_vector) { - // Add the dimension into the arguments vector - arguments_vector.push_back("--dimension=" + dimension); - - // Great Circle (GC) distance is only valid for 2D! - if (distance_type == "gcd" && (dimension == "3D" || dimension == "ST")) { - continue; - } - if (dimension == "ST" && computation == "tlr") { - continue; - } - if (dimension == "ST") { - random_device rd; - mt19937 gen(rd()); - uniform_int_distribution time_slot_distribution(1, 5); - arguments_vector.push_back("--time_slot=" + to_string(time_slot_distribution(gen))); - } - - string arguments_string; - for (const auto &j: arguments_vector) { - arguments_string += " " + j; - } - - cout << "(" << combination_number - << ") Testing the software with arguments: " + arguments_string << endl << flush; - - // Specify the executable path as the first argument - std::filesystem::path currentPath = - std::filesystem::current_path().parent_path().parent_path() / - "examples/end-to-end/Example_Data_Generation_Modeling_and_Prediction"; - std::string fullCommand = std::string(currentPath) + arguments_string; - if (std::system(fullCommand.c_str())) { - throw runtime_error("This test failed " + fullCommand); - } - - // To remove the previous dimension. - arguments_vector.pop_back(); - if (dimension == "ST") { - // To remove the time slot. - arguments_vector.pop_back(); - } - combination_number += 1; - } - } - } - } - } - } +vector dimensions_vector; +vector precision_vector = {"single", "double"}; +vector distance_vector = {"eg", "gcd"}; + +size_t combination_number = 1; +size_t number_of_iterations = 1; +for ( +int i = 0; +i arguments_vector; +// This helper function will fill the arguments vector with the software arguments commands +GenerateCommandLineArguments(current_kernel, computation, dimensions_vector, precision, + arguments_vector, distance_type +); +// Generate combination of dimensions. +for ( +const auto &dimension +: dimensions_vector) { +// Add the dimension into the arguments vector +arguments_vector.push_back("--dimension=" + dimension); + +// Great Circle (GC) distance is only valid for 2D! +if (distance_type == "gcd" && (dimension == "3D" || dimension == "ST")) { +continue; +} +if (dimension == "ST" && computation == "tlr") { +continue; +} +if (dimension == "ST") { +random_device rd; +mt19937 gen(rd()); +uniform_int_distribution time_slot_distribution(1, 5); +arguments_vector.push_back("--time_slot=" + +to_string(time_slot_distribution(gen) +)); +} + +string arguments_string; +for ( +const auto &j +: arguments_vector) { +arguments_string += " " + +j; +} + +cout << "(" << combination_number +<< ") Testing the software with arguments: " + arguments_string << endl << +flush; + +// Specify the executable path as the first argument +std::filesystem::path currentPath = + std::filesystem::current_path().parent_path().parent_path() / + "examples/end-to-end/Example_Data_Generation_Modeling_and_Prediction"; +std::string fullCommand = std::string(currentPath) + arguments_string; +if ( +std::system(fullCommand +. + +c_str() + +)) { +throw runtime_error("This test failed " + fullCommand); +} + +// To remove the previous dimension. +arguments_vector. + +pop_back(); + +if (dimension == "ST") { +// To remove the time slot. +arguments_vector. + +pop_back(); + +} +combination_number += 1; +} +} +} +} +} +} } diff --git a/tests/heavy-tests/README.md b/tests/heavy-tests/README.md index fe40887d..a880bd46 100644 --- a/tests/heavy-tests/README.md +++ b/tests/heavy-tests/README.md @@ -1,3 +1,4 @@ # Heavy Tests Subdirectory -This directory encompasses all the comprehensive tests for the project. It executes all project computations utilizing a range of technologies to guarantee the seamless functionality of every aspect. \ No newline at end of file +This directory encompasses all the comprehensive tests for the project. It executes all project computations utilizing a +range of technologies to guarantee the seamless functionality of every aspect. \ No newline at end of file From f8a670b26f2d90c2c3f809fd30cf82f82a2a6edb Mon Sep 17 00:00:00 2001 From: Mahmoud Karargy Date: Fri, 5 Apr 2024 05:43:30 +0300 Subject: [PATCH 46/82] potential for tlr issue --- .../concrete/TestHiCMAImplementationTLR.cpp | 92 ++++++++----------- 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp index efe3fd4f..caed6e9a 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp @@ -24,28 +24,27 @@ using namespace exageostat::configurations; //Test that the function initializes the HICMA_descriptorC descriptor correctly. void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { - Configurations synthetic_data_configurations; - synthetic_data_configurations.SetComputation(exageostat::common::TILE_LOW_RANK); - synthetic_data_configurations.SetMaxMleIterations(1); - synthetic_data_configurations.SetTolerance(4); - - vector lb{0.1, 0.1, 0.1}; - synthetic_data_configurations.SetLowerBounds(lb); - synthetic_data_configurations.SetStartingTheta(lb); - vector ub{5, 5, 5}; - synthetic_data_configurations.SetUpperBounds(ub); - vector initial_theta{1, 0.1, 0.5}; - synthetic_data_configurations.SetInitialTheta(initial_theta); - vector estimated_theta{-1, -1, -1}; - synthetic_data_configurations.SetEstimatedTheta(estimated_theta); - synthetic_data_configurations.SetKernelName("UnivariateMaternStationary"); - synthetic_data_configurations.SetMaxRank(500); - synthetic_data_configurations.SetProblemSize(16); - synthetic_data_configurations.SetLowTileSize(8); - synthetic_data_configurations.SetDenseTileSize(8); - - SECTION("With Approximation mode ON") - { + SECTION("descriptors values") { + + Configurations synthetic_data_configurations; + synthetic_data_configurations.SetComputation(exageostat::common::TILE_LOW_RANK); + synthetic_data_configurations.SetMaxMleIterations(1); + synthetic_data_configurations.SetTolerance(4); + + vector lb{0.1, 0.1, 0.1}; + synthetic_data_configurations.SetLowerBounds(lb); + synthetic_data_configurations.SetStartingTheta(lb); + vector ub{5, 5, 5}; + synthetic_data_configurations.SetUpperBounds(ub); + vector initial_theta{1, 0.1, 0.5}; + synthetic_data_configurations.SetInitialTheta(initial_theta); + vector estimated_theta{-1, -1, -1}; + synthetic_data_configurations.SetEstimatedTheta(estimated_theta); + synthetic_data_configurations.SetKernelName("UnivariateMaternStationary"); + synthetic_data_configurations.SetMaxRank(500); + synthetic_data_configurations.SetProblemSize(16); + synthetic_data_configurations.SetLowTileSize(8); + synthetic_data_configurations.SetDenseTileSize(8); // initialize Hardware. auto hardware = ExaGeoStatHardware(TILE_LOW_RANK, 1, 0); @@ -62,38 +61,20 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { int pGrid = synthetic_data_configurations.GetPGrid(); int qGrid = synthetic_data_configurations.GetQGrid(); - if (approximationMode == 1) { - // Descriptor C. - REQUIRE(HICMA_descriptorC->m == N); - REQUIRE(HICMA_descriptorC->n == N); - REQUIRE(HICMA_descriptorC->mb == lts); - REQUIRE(HICMA_descriptorC->nb == lts); - REQUIRE(HICMA_descriptorC->bsiz == lts * lts); - REQUIRE(HICMA_descriptorC->i == 0); - REQUIRE(HICMA_descriptorC->j == 0); - REQUIRE(HICMA_descriptorC->mt == ceil((N * 1.0) / (lts * 1.0))); - REQUIRE(HICMA_descriptorC->nt == ceil((N * 1.0) / (lts * 1.0))); - REQUIRE(HICMA_descriptorC->lm == N); - REQUIRE(HICMA_descriptorC->ln == N); - REQUIRE(HICMA_descriptorC->p == pGrid); - REQUIRE(HICMA_descriptorC->q == qGrid); - } - }SECTION("With Approximation mode OFF") { - synthetic_data_configurations.SetApproximationMode(0); - - // initialize Hardware. - auto hardware = ExaGeoStatHardware(TILE_LOW_RANK, 1, 0); - - std::unique_ptr> data; - exageostat::api::ExaGeoStat::ExaGeoStatLoadData(synthetic_data_configurations, data); - exageostat::api::ExaGeoStat::ExaGeoStatDataModeling(synthetic_data_configurations, data); - - int N = synthetic_data_configurations.GetProblemSize(); - int lts = synthetic_data_configurations.GetLowTileSize(); - int pGrid = synthetic_data_configurations.GetPGrid(); - int qGrid = synthetic_data_configurations.GetQGrid(); - - // Re-Run again but with approx mode OFF + // Descriptor C. + REQUIRE(HICMA_descriptorC->m == N); + REQUIRE(HICMA_descriptorC->n == N); + REQUIRE(HICMA_descriptorC->mb == lts); + REQUIRE(HICMA_descriptorC->nb == lts); + REQUIRE(HICMA_descriptorC->bsiz == lts * lts); + REQUIRE(HICMA_descriptorC->i == 0); + REQUIRE(HICMA_descriptorC->j == 0); + REQUIRE(HICMA_descriptorC->mt == ceil((N * 1.0) / (lts * 1.0))); + REQUIRE(HICMA_descriptorC->nt == ceil((N * 1.0) / (lts * 1.0))); + REQUIRE(HICMA_descriptorC->lm == N); + REQUIRE(HICMA_descriptorC->ln == N); + REQUIRE(HICMA_descriptorC->p == pGrid); + REQUIRE(HICMA_descriptorC->q == qGrid); int maxRank = synthetic_data_configurations.GetMaxRank(); string actualObservationsFilePath = synthetic_data_configurations.GetActualObservationsFilePath(); @@ -208,10 +189,9 @@ void TEST_HICMA_DESCRIPTORS_VALUES_TLR() { REQUIRE(HICMA_descriptorDeterminant->ln == 1); REQUIRE(HICMA_descriptorDeterminant->p == pGrid); REQUIRE(HICMA_descriptorDeterminant->q == qGrid); - } } TEST_CASE("HiCMA Implementation TLR") { TEST_HICMA_DESCRIPTORS_VALUES_TLR(); -} \ No newline at end of file +} From 3e47fb1319ddcfa0d8ab0b04e64996af17b67266 Mon Sep 17 00:00:00 2001 From: mahmoudElkarargyBS Date: Sun, 14 Apr 2024 18:24:23 +0300 Subject: [PATCH 47/82] updates in heavy tests --- tests/heavy-tests/HeavyTests.cpp | 234 ++++++++++++++++--------------- 1 file changed, 118 insertions(+), 116 deletions(-) diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index 2cd0563c..e9b3a1aa 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -30,8 +30,8 @@ using namespace exageostat::dataunits; using namespace exageostat::api; using namespace exageostat::kernels; -void GenerateCommandLineArguments(const string &aKernelName, const string &aComputation, vector &aDimensions, - const string &aPrecision, vector &arguments_vector, +void GenerateCommandLineArguments(const string &aKernelName, const string &aComputation, vector &aDimensions, + const string &aPrecision, vector &arguments_vector, const string &aDistanceType) { // Search for the substring "TimeSpace" in the kernel name, As ST dimension only works with kernels having Spacetime in their name. @@ -52,7 +52,7 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp int lts = INT16_MAX; uniform_int_distribution problem_size_distribution(LOWER_SIZE, LOWER_SIZE * MULTIPLICATION); int max_threads_size = static_cast(std::thread::hardware_concurrency()); - uniform_int_distribution cpu_size_distribution(1, max_threads_size - 1); + uniform_int_distribution cpu_size_distribution(1, max_threads_size - 5); uniform_int_distribution max_iteration_distribution(1, 4); uniform_int_distribution band_distribution(1, 6); uniform_int_distribution tolerance_distribution(1, 5); @@ -75,6 +75,7 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp uniform_int_distribution dts_distribution(LOWER_SIZE, min(N_value, lts)); int dts = dts_distribution(gen); + // Since both Bivariate and Trivariate requires specific values. if (aKernelName.find("Bivariate") != string::npos || aKernelName.find("Trivariate") != string::npos) { dts = 18; N_value = 162; @@ -87,7 +88,7 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp uniform_int_distribution zMiss_distribution(2, min(N_value / 2, 100)); // Create a kernel object to get the number of parameters for each kernel. - Kernel *pKernel = exageostat::plugins::PluginRegistry < Kernel < double >> ::Create(aKernelName, 1); + Kernel *pKernel = exageostat::plugins::PluginRegistry>::Create(aKernelName, 1); int param_number = pKernel->GetParametersNumbers(); delete pKernel; @@ -168,119 +169,120 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp TEST_CASE("END-TO-END") { // Add all the possible combination of code. -vector kernels_vector = { - "BivariateMaternFlexible", - "BivariateMaternParsimonious", - "BivariateSpacetimeMaternStationary", - "TrivariateMaternParsimonious", - "UnivariateExpNonGaussian", - "UnivariateMaternNonGaussian", - "UnivariateMaternNuggetsStationary", - "UnivariateMaternStationary", - "UnivariateSpacetimeMaternStationary" -}; - -vector computation_vector = {"exact", "diag_approx"}; + vector kernels_vector = { + "BivariateMaternFlexible", + "BivariateMaternParsimonious", + "BivariateSpacetimeMaternStationary", + "TrivariateMaternParsimonious", + "UnivariateExpNonGaussian", + "UnivariateMaternNonGaussian", + "UnivariateMaternNuggetsStationary", + "UnivariateMaternStationary", + "UnivariateSpacetimeMaternStationary" + }; + + vector computation_vector = {"exact", "diag_approx"}; #ifdef USE_HICMA -computation_vector.emplace_back("tlr"); + computation_vector.emplace_back("tlr"); #endif -vector dimensions_vector; -vector precision_vector = {"single", "double"}; -vector distance_vector = {"eg", "gcd"}; - -size_t combination_number = 1; -size_t number_of_iterations = 1; -for ( -int i = 0; -i arguments_vector; -// This helper function will fill the arguments vector with the software arguments commands -GenerateCommandLineArguments(current_kernel, computation, dimensions_vector, precision, - arguments_vector, distance_type -); -// Generate combination of dimensions. -for ( -const auto &dimension -: dimensions_vector) { -// Add the dimension into the arguments vector -arguments_vector.push_back("--dimension=" + dimension); - -// Great Circle (GC) distance is only valid for 2D! -if (distance_type == "gcd" && (dimension == "3D" || dimension == "ST")) { -continue; -} -if (dimension == "ST" && computation == "tlr") { -continue; -} -if (dimension == "ST") { -random_device rd; -mt19937 gen(rd()); -uniform_int_distribution time_slot_distribution(1, 5); -arguments_vector.push_back("--time_slot=" + -to_string(time_slot_distribution(gen) -)); -} - -string arguments_string; -for ( -const auto &j -: arguments_vector) { -arguments_string += " " + -j; -} - -cout << "(" << combination_number -<< ") Testing the software with arguments: " + arguments_string << endl << -flush; - -// Specify the executable path as the first argument -std::filesystem::path currentPath = - std::filesystem::current_path().parent_path().parent_path() / - "examples/end-to-end/Example_Data_Generation_Modeling_and_Prediction"; -std::string fullCommand = std::string(currentPath) + arguments_string; -if ( -std::system(fullCommand -. - -c_str() - -)) { -throw runtime_error("This test failed " + fullCommand); + vector dimensions_vector; + vector precision_vector = {"single", "double"}; + vector distance_vector = {"eg", "gcd"}; + + size_t combination_number = 1; + size_t number_of_iterations = 1; + for ( + int i = 0; + i < number_of_iterations; + i++) { + cout << "**** END-TO_END TESTS -- ITERATION NUMBER (" << i + 1 << ") ****" << + endl; + // Generate combinations. + for ( + const auto ¤t_kernel + : kernels_vector) { + for ( + const auto &computation + : computation_vector) { + for ( + const auto &precision + : precision_vector) { + for ( + const auto &distance_type + : distance_vector) { + vector arguments_vector; + // This helper function will fill the arguments vector with the software arguments commands + GenerateCommandLineArguments(current_kernel, computation, dimensions_vector, precision, + arguments_vector, distance_type + ); + // Generate combination of dimensions. + for ( + const auto &dimension + : dimensions_vector) { + // Add the dimension into the arguments vector + arguments_vector.push_back("--dimension=" + dimension); + + // Great Circle (GC) distance is only valid for 2D! + if (distance_type == "gcd" && (dimension == "3D" || dimension == "ST")) { + continue; + } + if (dimension == "ST" && computation == "tlr") { + continue; + } + if (dimension == "ST") { + random_device rd; + mt19937 gen(rd()); + uniform_int_distribution time_slot_distribution(1, 5); + arguments_vector.push_back("--time_slot=" + + to_string(time_slot_distribution(gen) + )); + } + + string arguments_string; + for ( + const auto &j + : arguments_vector) { + arguments_string += " " + + j; + } + + cout << "(" << combination_number + << ") Testing the software with arguments: " + arguments_string << endl << + flush; + + // Specify the executable path as the first argument + std::filesystem::path currentPath = + std::filesystem::current_path().parent_path().parent_path() / + "examples/end-to-end/Example_Data_Generation_Modeling_and_Prediction"; + std::string fullCommand = std::string(currentPath) + arguments_string; + if ( + std::system(fullCommand + . + + c_str() + + )) { + throw runtime_error("This test failed " + fullCommand); + } + + // To remove the previous dimension. + arguments_vector. + + pop_back(); + + if (dimension == "ST") { + // To remove the time slot. + arguments_vector. + + pop_back(); + + } + combination_number += 1; + } + } + } + } + } + } } -// To remove the previous dimension. -arguments_vector. - -pop_back(); - -if (dimension == "ST") { -// To remove the time slot. -arguments_vector. - -pop_back(); - -} -combination_number += 1; -} -} -} -} -} -} -} From 8480f4f846289a5a9cee943bd5fca5b72d0a8039 Mon Sep 17 00:00:00 2001 From: mahmoudElkarargyBS Date: Sun, 14 Apr 2024 21:51:09 +0300 Subject: [PATCH 48/82] a fix for gpu in gaussian kernels --- src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp b/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp index af873b51..4250482e 100644 --- a/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp +++ b/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp @@ -29,10 +29,10 @@ struct starpu_codelet GaussianCodelet::cl_gaussian_to_non = { #else .where = STARPU_CPU, .cpu_funcs = {cl_gaussian_to_non_function}, - .nbuffers = 1, +#endif + .nbuffers = 1, .modes = {STARPU_RW}, .name = "gaussian_to_non" -#endif }; template @@ -93,3 +93,4 @@ void GaussianCodelet::core_gaussian_to_non(T *apDescriptorZ, const T *apLocal } } + From ccacbf905af091233e7cd81b9a7bd43eb7f903d6 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 15 Apr 2024 19:03:54 +0200 Subject: [PATCH 49/82] smalling tlr size --- .../concrete/gaussian-to-non-codelet.cpp | 2 + tests/heavy-tests/HeavyTests.cpp | 79 ++++++------------- 2 files changed, 24 insertions(+), 57 deletions(-) diff --git a/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp b/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp index 4250482e..ccfb2f78 100644 --- a/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp +++ b/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp @@ -29,6 +29,8 @@ struct starpu_codelet GaussianCodelet::cl_gaussian_to_non = { #else .where = STARPU_CPU, .cpu_funcs = {cl_gaussian_to_non_function}, + .cuda_funcs={}, + .cuda_flags={0}, #endif .nbuffers = 1, .modes = {STARPU_RW}, diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index e9b3a1aa..c46c23a1 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -13,7 +13,6 @@ #include #include -#include #ifdef USE_CUDA #include @@ -21,13 +20,10 @@ #include -#include #include using namespace std; -using namespace exageostat::dataunits; -using namespace exageostat::api; using namespace exageostat::kernels; void GenerateCommandLineArguments(const string &aKernelName, const string &aComputation, vector &aDimensions, @@ -62,7 +58,7 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp int N_value = problem_size_distribution(gen); if (aComputation == "tlr") { - uniform_int_distribution lts_distribution(LOWER_SIZE, N_value); + uniform_int_distribution lts_distribution(LOWER_SIZE, N_value / 2); lts = lts_distribution(gen); N_value = lts * lts; if (aKernelName.find("Bivariate") != string::npos || aKernelName.find("Trivariate") != string::npos) { @@ -168,7 +164,7 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp TEST_CASE("END-TO-END") { -// Add all the possible combination of code. + // Add all the possible combination of code. vector kernels_vector = { "BivariateMaternFlexible", "BivariateMaternParsimonious", @@ -191,38 +187,25 @@ TEST_CASE("END-TO-END") { size_t combination_number = 1; size_t number_of_iterations = 1; - for ( - int i = 0; - i < number_of_iterations; - i++) { - cout << "**** END-TO_END TESTS -- ITERATION NUMBER (" << i + 1 << ") ****" << - endl; + for (int i = 0; i < number_of_iterations; i++) { + cout << "**** END-TO_END TESTS -- ITERATION NUMBER (" << i + 1 << ") ****" << endl; + // Generate combinations. - for ( - const auto ¤t_kernel - : kernels_vector) { - for ( - const auto &computation - : computation_vector) { - for ( - const auto &precision - : precision_vector) { - for ( - const auto &distance_type - : distance_vector) { + for (const auto ¤t_kernel: kernels_vector) { + for (const auto &computation: computation_vector) { + for (const auto &precision: precision_vector) { + for (const auto &distance_type: distance_vector) { + vector arguments_vector; // This helper function will fill the arguments vector with the software arguments commands GenerateCommandLineArguments(current_kernel, computation, dimensions_vector, precision, - arguments_vector, distance_type - ); + arguments_vector, distance_type); // Generate combination of dimensions. - for ( - const auto &dimension - : dimensions_vector) { - // Add the dimension into the arguments vector + for (const auto &dimension: dimensions_vector) { + // Add the dimension into the arguments vector arguments_vector.push_back("--dimension=" + dimension); - // Great Circle (GC) distance is only valid for 2D! + // Great Circle (GC) distance is only valid for 2D! if (distance_type == "gcd" && (dimension == "3D" || dimension == "ST")) { continue; } @@ -233,49 +216,31 @@ TEST_CASE("END-TO-END") { random_device rd; mt19937 gen(rd()); uniform_int_distribution time_slot_distribution(1, 5); - arguments_vector.push_back("--time_slot=" + - to_string(time_slot_distribution(gen) - )); + arguments_vector.push_back("--time_slot=" + to_string(time_slot_distribution(gen))); } string arguments_string; - for ( - const auto &j - : arguments_vector) { - arguments_string += " " + - j; + for (const auto &j: arguments_vector) { + arguments_string += " " + j; } cout << "(" << combination_number - << ") Testing the software with arguments: " + arguments_string << endl << - flush; + << ") Testing the software with arguments: " + arguments_string << endl << flush; // Specify the executable path as the first argument std::filesystem::path currentPath = std::filesystem::current_path().parent_path().parent_path() / "examples/end-to-end/Example_Data_Generation_Modeling_and_Prediction"; std::string fullCommand = std::string(currentPath) + arguments_string; - if ( - std::system(fullCommand - . - - c_str() - - )) { + if (std::system(fullCommand.c_str())) { throw runtime_error("This test failed " + fullCommand); } - // To remove the previous dimension. - arguments_vector. - - pop_back(); + arguments_vector.pop_back(); if (dimension == "ST") { - // To remove the time slot. - arguments_vector. - - pop_back(); - + // To remove the time slot. + arguments_vector.pop_back(); } combination_number += 1; } From d268ca3abb5f30576f273608f4abdfbe50f3a79d Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 15 Apr 2024 19:28:06 +0200 Subject: [PATCH 50/82] update sizes of heavy_tests --- tests/heavy-tests/HeavyTests.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index c46c23a1..3e70093c 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -43,10 +43,11 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp // Set up a random number generator random_device rd; mt19937 gen(rd()); - int LOWER_SIZE = 5; - int MULTIPLICATION = 5; + int LOWER_SIZE = 6; + int MAX_SIZE = 256; + int MAX_LTS = 20; int lts = INT16_MAX; - uniform_int_distribution problem_size_distribution(LOWER_SIZE, LOWER_SIZE * MULTIPLICATION); + uniform_int_distribution problem_size_distribution(LOWER_SIZE, MAX_SIZE); int max_threads_size = static_cast(std::thread::hardware_concurrency()); uniform_int_distribution cpu_size_distribution(1, max_threads_size - 5); uniform_int_distribution max_iteration_distribution(1, 4); @@ -58,7 +59,7 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp int N_value = problem_size_distribution(gen); if (aComputation == "tlr") { - uniform_int_distribution lts_distribution(LOWER_SIZE, N_value / 2); + uniform_int_distribution lts_distribution(LOWER_SIZE, MAX_LTS); lts = lts_distribution(gen); N_value = lts * lts; if (aKernelName.find("Bivariate") != string::npos || aKernelName.find("Trivariate") != string::npos) { @@ -167,13 +168,13 @@ TEST_CASE("END-TO-END") { // Add all the possible combination of code. vector kernels_vector = { "BivariateMaternFlexible", + "UnivariateMaternStationary", "BivariateMaternParsimonious", "BivariateSpacetimeMaternStationary", "TrivariateMaternParsimonious", "UnivariateExpNonGaussian", "UnivariateMaternNonGaussian", "UnivariateMaternNuggetsStationary", - "UnivariateMaternStationary", "UnivariateSpacetimeMaternStationary" }; From 115c0a48da912ce8a4ebd470f6e51cf04b06727c Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 15 Apr 2024 20:36:26 +0200 Subject: [PATCH 51/82] smalling size for gpu --- tests/heavy-tests/HeavyTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index 3e70093c..98ca2f2e 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -44,8 +44,8 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp random_device rd; mt19937 gen(rd()); int LOWER_SIZE = 6; - int MAX_SIZE = 256; - int MAX_LTS = 20; + int MAX_SIZE = 25; + int MAX_LTS = 18; int lts = INT16_MAX; uniform_int_distribution problem_size_distribution(LOWER_SIZE, MAX_SIZE); int max_threads_size = static_cast(std::thread::hardware_concurrency()); From 128af204997faea7b60cf7fb6750290ba4c4bf22 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Tue, 16 Apr 2024 01:19:39 +0200 Subject: [PATCH 52/82] minior changes --- CMakeLists.txt | 2 -- cmake/ImportChameleon.cmake | 1 + cmake/ImportHiCMA.cmake | 2 ++ tests/heavy-tests/HeavyTests.cpp | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff95aa91..344174f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,8 +63,6 @@ enable_language(CXX) # Get the current path of the project. add_compile_definitions(PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}/") - - # ExaGeoStatCPP depends on a CUDA # ------------------------------- if (USE_CUDA) diff --git a/cmake/ImportChameleon.cmake b/cmake/ImportChameleon.cmake index 1b2db765..b72df9ba 100644 --- a/cmake/ImportChameleon.cmake +++ b/cmake/ImportChameleon.cmake @@ -39,6 +39,7 @@ ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_ # The AFTER keyword specifies that these directories should be searched after the default ones. include_directories(AFTER ${CHAMELEON_DIR_FOUND}/include/coreblas) include_directories(${CHAMELEON_DIR_FOUND}/chameleon-src) +include_directories(${CHAMELEON_DIR_FOUND}) # Print a status message indicating the completion of Chameleon's inclusion process. message(STATUS "${name} done") diff --git a/cmake/ImportHiCMA.cmake b/cmake/ImportHiCMA.cmake index b6967f02..532de60d 100644 --- a/cmake/ImportHiCMA.cmake +++ b/cmake/ImportHiCMA.cmake @@ -35,6 +35,8 @@ ImportDependency(${name} ${tag} ${version} ${url} "${flag}" "" ${is_cmake} ${is_ # Directories containing HiCMA headers are included in the project, ensuring that HiCMA's functions and types are accessible. include_directories(${HICMA_LIBDIR}/../hicma-src/hicma_ext) +include_directories(${HICMA_LIBDIR}/../hicma_ext) + # A status message is displayed to indicate the successful inclusion of the HiCMA library into the project. message(STATUS "HiCMA done") diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index 98ca2f2e..6d2f68f1 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -136,7 +136,7 @@ void GenerateCommandLineArguments(const string &aKernelName, const string &aComp #ifdef USE_CUDA int nDevices; cudaGetDeviceCount(&nDevices); - uniform_int_distribution gpu_size_distribution(1, nDevices); + uniform_int_distribution gpu_size_distribution(0, nDevices - 1); arguments_vector.push_back("--gpus=" + to_string(gpu_size_distribution(gen))); #endif From a629ef3f4168d8e16043294f8eba9586d7669cc6 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 21 Apr 2024 16:37:46 +0200 Subject: [PATCH 53/82] Huge refactoring in docs --- CMakeLists.txt | 2 +- DESCRIPTION | 2 +- ExaGeoStatCPPConfig.cmake.in | 2 +- LICENSE | 2 +- README.md | 236 +++++++----------- USER_MANUAL.md | 175 +++++++------ clean_build.sh | 2 +- cmake/FindR.cmake | 2 +- cmake/ImportBLAS.cmake | 2 +- cmake/ImportBLASPP.cmake | 2 +- cmake/ImportCatch2.cmake | 2 +- cmake/ImportChameleon.cmake | 2 +- cmake/ImportGSL.cmake | 2 +- cmake/ImportHCore.cmake | 2 +- cmake/ImportHiCMA.cmake | 2 +- cmake/ImportHwloc.cmake | 2 +- cmake/ImportLapack.cmake | 2 +- cmake/ImportNLOPT.cmake | 2 +- cmake/ImportStarPu.cmake | 2 +- cmake/ImportStarsH.cmake | 2 +- cmake/macros/BuildDependency.cmake | 2 +- cmake/macros/ImportDependency.cmake | 2 +- cmake/toolchains/CudaToolchain.cmake | 2 +- cmake/toolchains/GccToolchain.cmake | 2 +- configure | 3 +- docs/CMakeLists.txt | 2 +- docs/ExaGeoStat-CPP-Manual.pdf | Bin 0 -> 1075902 bytes docs/ExaGeoStat-R-Interface-Manual.pdf | Bin 0 -> 119253 bytes examples/CMakeLists.txt | 2 +- examples/configurations/CMakeLists.txt | 2 +- .../RunningWithDifferentConfigurations.cpp | 2 +- .../configurations/SetupConfigurations.cpp | 2 +- examples/data-generators/CMakeLists.txt | 2 +- .../SyntheticDataGeneration.cpp | 2 +- examples/data-loader/CMakeLists.txt | 2 +- examples/data-loader/CSVLoader.cpp | 2 +- examples/descriptors/CMakeLists.txt | 2 +- examples/descriptors/ChameleonDescriptor.cpp | 2 +- .../descriptors/ChameleonToHicmaConverter.cpp | 2 +- examples/descriptors/HicmaDescriptor.cpp | 2 +- examples/end-to-end/CMakeLists.txt | 2 +- examples/end-to-end/DataGeneration.cpp | 2 +- .../end-to-end/DataGenerationAndModeling.cpp | 2 +- .../DataGenerationAndPrediction.cpp | 2 +- .../DataGenerationModelingAndPrediction.cpp | 2 +- examples/end-to-end/DataModeling.cpp | 2 +- examples/end-to-end/DataPrediction.cpp | 2 +- examples/hardware/CMakeLists.txt | 2 +- examples/hardware/ExaGeoStatHardware.cpp | 2 +- .../Rcpp-adapters/FunctionsAdapter.hpp | 2 +- inst/include/api/ExaGeoStat.hpp | 2 +- inst/include/common/Definitions.hpp | 2 +- inst/include/common/PluginRegistry.hpp | 2 +- .../include/configurations/Configurations.hpp | 2 +- .../include/data-generators/DataGenerator.hpp | 2 +- .../data-generators/LocationGenerator.hpp | 2 +- .../concrete/SyntheticGenerator.hpp | 2 +- inst/include/data-loader/DataLoader.hpp | 2 +- .../data-loader/concrete/CSVLoader.hpp | 2 +- inst/include/data-units/DescriptorData.hpp | 2 +- inst/include/data-units/ExaGeoStatData.hpp | 2 +- inst/include/data-units/Locations.hpp | 2 +- .../descriptor/ExaGeoStatDescriptor.hpp | 2 +- .../concrete/ChameleonDescriptor.hpp | 2 +- .../descriptor/concrete/HicmaDescriptor.hpp | 2 +- inst/include/hardware/ExaGeoStatHardware.hpp | 2 +- inst/include/helpers/BasselFunction.hpp | 2 +- inst/include/helpers/ByteHandler.hpp | 2 +- inst/include/helpers/CommunicatorMPI.hpp | 2 +- .../helpers/DistanceCalculationHelpers.hpp | 2 +- inst/include/kernels/Kernel.hpp | 2 +- .../concrete/BivariateMaternFlexible.hpp | 2 +- .../concrete/BivariateMaternParsimonious.hpp | 2 +- .../BivariateSpacetimeMaternStationary.hpp | 2 +- .../concrete/TrivariateMaternParsimonious.hpp | 2 +- .../concrete/UnivariateExpNonGaussian.hpp | 2 +- .../concrete/UnivariateMaternDbeta.hpp | 2 +- .../concrete/UnivariateMaternDdbetaBeta.hpp | 2 +- .../concrete/UnivariateMaternDdbetaNu.hpp | 2 +- .../concrete/UnivariateMaternDdnuNu.hpp | 2 +- .../UnivariateMaternDdsigmaSquare.hpp | 2 +- .../UnivariateMaternDdsigmaSquareBeta.hpp | 2 +- .../UnivariateMaternDdsigmaSquareNu.hpp | 2 +- .../kernels/concrete/UnivariateMaternDnu.hpp | 2 +- .../concrete/UnivariateMaternDsigmaSquare.hpp | 2 +- .../concrete/UnivariateMaternNonGaussian.hpp | 2 +- .../UnivariateMaternNuggetsStationary.hpp | 2 +- .../concrete/UnivariateMaternStationary.hpp | 2 +- .../concrete/UnivariatePowExpStationary.hpp | 2 +- .../UnivariateSpacetimeMaternStationary.hpp | 2 +- .../LinearAlgebraFactory.hpp | 2 +- .../LinearAlgebraMethods.hpp | 2 +- .../chameleon/ChameleonImplementation.hpp | 2 +- .../chameleon/dense/ChameleonDense.hpp | 2 +- .../concrete/chameleon/dst/ChameleonDST.hpp | 2 +- .../hicma/tlr/HicmaImplementation.hpp | 2 +- inst/include/prediction/Prediction.hpp | 2 +- .../PredictionAuxiliaryFunctions.hpp | 2 +- inst/include/prediction/PredictionHelpers.hpp | 2 +- inst/include/results/Results.hpp | 2 +- inst/include/runtime/RuntimeFunctions.hpp | 2 +- .../runtime/starpu/StarPuCodeletsHeaders.hpp | 2 +- .../runtime/starpu/concrete/dcmg-codelet.hpp | 2 +- .../runtime/starpu/concrete/ddotp-codelet.hpp | 2 +- .../runtime/starpu/concrete/dmdet-codelet.hpp | 2 +- .../starpu/concrete/dmloe-mmom-codelet.hpp | 2 +- .../concrete/dmse-bivariate-codelet.hpp | 2 +- .../runtime/starpu/concrete/dmse-codelet.hpp | 2 +- .../starpu/concrete/dtrace-codelet.hpp | 2 +- .../runtime/starpu/concrete/dzcpy-codelet.hpp | 2 +- .../concrete/gaussian-to-non-codelet.hpp | 2 +- .../concrete/non-gaussian-loglike-codelet.hpp | 2 +- .../non-gaussian-transform-codelet.hpp | 2 +- .../starpu/concrete/stride-vec-codelet.hpp | 2 +- .../concrete/tri-stride-vec-codelet.hpp | 2 +- .../runtime/starpu/helpers/StarPuHelpers.hpp | 2 +- .../starpu/helpers/StarPuHelpersFactory.hpp | 2 +- .../concrete/ChameleonStarPuHelpers.hpp | 2 +- .../helpers/concrete/HicmaStarPuHelpers.hpp | 2 +- inst/include/utilities/EnumStringParser.hpp | 2 +- inst/include/utilities/ErrorHandler.hpp | 2 +- inst/include/utilities/Logger.hpp | 2 +- man/Data.Rd | 4 +- man/Hardware.Rd | 4 +- man/fisher.Rd | 4 +- man/get_Z_measurement_vector.Rd | 4 +- man/get_locationsX.Rd | 4 +- man/get_locationsY.Rd | 4 +- man/get_locationsZ.Rd | 4 +- man/idw.Rd | 4 +- man/mloe_mmom.Rd | 4 +- man/model_data.Rd | 4 +- man/predict_data.Rd | 4 +- man/simulate_data.Rd | 4 +- scripts/Benchmarking.sh | 2 +- src/CMakeLists.txt | 2 +- src/Makefile | 2 +- src/Rcpp-adapters/CMakeLists.txt | 2 +- src/Rcpp-adapters/FunctionsAdapter.cpp | 2 +- src/Rcpp-adapters/RcppExports.cpp | 2 +- src/Rcpp-adapters/RcppModules.cpp | 2 +- src/api/CMakeLists.txt | 2 +- src/api/ExaGeoStat.cpp | 2 +- src/configurations/CMakeLists.txt | 2 +- src/configurations/Configurations.cpp | 2 +- src/data-generators/CMakeLists.txt | 2 +- src/data-generators/DataGenerator.cpp | 2 +- src/data-generators/LocationGenerator.cpp | 2 +- src/data-generators/concrete/CMakeLists.txt | 2 +- .../concrete/SyntheticGenerator.cpp | 2 +- src/data-loader/CMakeLists.txt | 2 +- src/data-loader/DataLoader.cpp | 2 +- src/data-loader/concrete/CMakeLists.txt | 2 +- src/data-loader/concrete/CSVLoader.cpp | 2 +- src/data-units/CMakeLists.txt | 2 +- src/data-units/DescriptorData.cpp | 2 +- src/data-units/ExaGeoStatData.cpp | 2 +- src/data-units/Locations.cpp | 2 +- src/data-units/descriptor/CMakeLists.txt | 2 +- .../descriptor/ExaGeoStatDescriptor.cpp | 2 +- .../descriptor/concrete/CMakeLists.txt | 2 +- .../concrete/ChameleonDescriptor.cpp | 2 +- .../descriptor/concrete/HicmaDescriptor.cpp | 2 +- src/hardware/CMakeLists.txt | 2 +- src/hardware/ExaGeoStatHardware.cpp | 2 +- src/helpers/BasselFunction.cpp | 2 +- src/helpers/ByteHandler.cpp | 2 +- src/helpers/CMakeLists.txt | 2 +- src/helpers/CommunicatorMPI.cpp | 2 +- src/helpers/DistanceCalculationHelpers.cpp | 2 +- src/kernels/CMakeLists.txt | 2 +- src/kernels/Kernel.cpp | 2 +- .../concrete/BivariateMaternFlexible.cpp | 2 +- .../concrete/BivariateMaternParsimonious.cpp | 2 +- .../BivariateSpacetimeMaternStationary.cpp | 2 +- .../concrete/TrivariateMaternParsimonious.cpp | 2 +- .../concrete/UnivariateExpNonGaussian.cpp | 2 +- .../concrete/UnivariateMaternDbeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaBeta.cpp | 2 +- .../concrete/UnivariateMaternDdbetaNu.cpp | 2 +- .../concrete/UnivariateMaternDdnuNu.cpp | 2 +- .../UnivariateMaternDdsigmaSquare.cpp | 2 +- .../UnivariateMaternDdsigmaSquareBeta.cpp | 2 +- .../UnivariateMaternDdsigmaSquareNu.cpp | 2 +- src/kernels/concrete/UnivariateMaternDnu.cpp | 2 +- .../concrete/UnivariateMaternDsigmaSquare.cpp | 2 +- .../concrete/UnivariateMaternNonGaussian.cpp | 2 +- .../UnivariateMaternNuggetsStationary.cpp | 2 +- .../concrete/UnivariateMaternStationary.cpp | 2 +- .../concrete/UnivariatePowExpStationary.cpp | 2 +- .../UnivariateSpacetimeMaternStationary.cpp | 2 +- src/linear-algebra-solvers/CMakeLists.txt | 2 +- .../LinearAlgebraFactory.cpp | 2 +- .../LinearAlgebraMethods.cpp | 2 +- .../concrete/CMakeLists.txt | 2 +- .../chameleon/ChameleonImplementation.cpp | 2 +- .../chameleon/dense/ChameleonDense.cpp | 2 +- .../concrete/chameleon/dst/ChameleonDST.cpp | 2 +- .../concrete/tlr/HicmaImplementation.cpp | 2 +- src/prediction/CMakeLists.txt | 2 +- src/prediction/Prediction.cpp | 2 +- .../PredictionAuxiliaryFunctions.cpp | 2 +- src/prediction/PredictionHelpers.cpp | 2 +- src/results/CMakeLists.txt | 2 +- src/results/Results.cpp | 2 +- src/runtime/CMakeLists.txt | 2 +- src/runtime/parsec/CMakeLists.txt | 2 +- src/runtime/parsec/ParsecFunctions.cpp | 2 +- src/runtime/starpu/CMakeLists.txt | 4 +- src/runtime/starpu/StarPuFunctions.cpp | 2 +- src/runtime/starpu/concrete/dcmg-codelet.cpp | 2 +- src/runtime/starpu/concrete/ddotp-codelet.cpp | 2 +- src/runtime/starpu/concrete/dmdet-codelet.cpp | 2 +- .../starpu/concrete/dmloe-mmom-codelet.cpp | 2 +- .../concrete/dmse-bivariate-codelet.cpp | 2 +- src/runtime/starpu/concrete/dmse-codelet.cpp | 2 +- .../starpu/concrete/dtrace-codelet.cpp | 2 +- src/runtime/starpu/concrete/dzcpy-codelet.cpp | 2 +- .../concrete/gaussian-to-non-codelet.cpp | 2 +- .../concrete/non-gaussian-loglike-codelet.cpp | 2 +- .../non-gaussian-transform-codelet.cpp | 2 +- .../starpu/concrete/stride-vec-codelet.cpp | 2 +- .../concrete/tri-stride-vec-codelet.cpp | 2 +- src/runtime/starpu/helpers/CMakeLists.txt | 2 +- .../starpu/helpers/StarPuHelpersFactory.cpp | 2 +- .../concrete/ChameleonStarPuHelpers.cpp | 2 +- .../helpers/concrete/HicmaStarPuHelpers.cpp | 2 +- tests/R-tests/TestDataGeneration.R | 2 +- tests/R-tests/TestDataModeling.R | 2 +- tests/R-tests/TestDataPrediction.R | 2 +- tests/R-tests/TestExaGeoStatAPI.R | 2 +- tests/cpp-tests/CMakeLists.txt | 2 +- tests/cpp-tests/Rcpp-adapters/CMakeLists.txt | 2 +- .../Rcpp-adapters/TestAllRFunctions.cpp | 2 +- tests/cpp-tests/api/CMakeLists.txt | 2 +- tests/cpp-tests/api/TestExaGeoStatApi.cpp | 2 +- tests/cpp-tests/configurations/CMakeLists.txt | 2 +- .../configurations/TestConfigurations.cpp | 2 +- .../cpp-tests/data-generators/CMakeLists.txt | 2 +- .../concrete/TestCSVDataGenerator.cpp | 2 +- .../concrete/TestSyntheticGenerator.cpp | 2 +- tests/cpp-tests/data-units/CMakeLists.txt | 2 +- .../data-units/TestDescriptorData.cpp | 2 +- tests/cpp-tests/hardware/CMakeLists.txt | 2 +- .../hardware/TestExaGeoStatHardware.cpp | 2 +- tests/cpp-tests/helpers/CMakeLists.txt | 2 +- tests/cpp-tests/helpers/TestDiskWriter.cpp | 2 +- .../TestDistanceCalculationHelpers.cpp | 2 +- tests/cpp-tests/kernels/CMakeLists.txt | 2 +- .../concrete/TestBivariateMaternFlexible.cpp | 2 +- .../TestBivariateMaternParsimonious.cpp | 2 +- ...TestBivariateSpacetimeMaternStationary.cpp | 2 +- .../TestTrivariateMaternParsimonious.cpp | 2 +- .../concrete/TestUnivariateExpNonGaussian.cpp | 2 +- .../concrete/TestUnivariateMaternDbeta.cpp | 2 +- .../TestUnivariateMaternDdbetaBeta.cpp | 2 +- .../concrete/TestUnivariateMaternDdbetaNu.cpp | 2 +- .../concrete/TestUnivariateMaternDdnuNu.cpp | 2 +- .../TestUnivariateMaternDdsigmaSquare.cpp | 2 +- .../TestUnivariateMaternDdsigmaSquareBeta.cpp | 2 +- .../TestUnivariateMaternDdsigmaSquareNu.cpp | 2 +- .../concrete/TestUnivariateMaternDnu.cpp | 2 +- .../TestUnivariateMaternDsigmaSquare.cpp | 2 +- .../TestUnivariateMaternNonGaussian.cpp | 2 +- .../TestUnivariateMaternNuggetsStationary.cpp | 2 +- .../TestUnivariateMaternStationary.cpp | 2 +- .../TestUnivariatePowExpStationary.cpp | 2 +- ...estUnivariateSpacetimeMaternStationary.cpp | 2 +- .../linear-algebra-solvers/CMakeLists.txt | 2 +- .../TestChameleonImplementationDST.cpp | 2 +- .../TestChameleonImplementationDense.cpp | 2 +- .../concrete/TestHiCMAImplementationTLR.cpp | 2 +- tests/cpp-tests/prediction/CMakeLists.txt | 2 +- tests/cpp-tests/prediction/TestPrediction.cpp | 2 +- .../prediction/TestPredictionHelpers.cpp | 2 +- tests/cpp-tests/results/CMakeLists.txt | 2 +- tests/cpp-tests/results/TestResults.cpp | 2 +- tests/heavy-tests/CMakeLists.txt | 2 +- tests/heavy-tests/ExamplesTests.cpp | 2 +- tests/heavy-tests/HeavyTests.cpp | 2 +- tests/heavy-tests/README.md | 3 +- 281 files changed, 481 insertions(+), 512 deletions(-) create mode 100644 docs/ExaGeoStat-CPP-Manual.pdf create mode 100644 docs/ExaGeoStat-R-Interface-Manual.pdf diff --git a/CMakeLists.txt b/CMakeLists.txt index 344174f7..0e9a3cc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/DESCRIPTION b/DESCRIPTION index bafc2a8f..1e6f8fee 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -5,7 +5,7 @@ Version: 1.1.0 Date: 2024-01-14 Author: Mahmoud ElKarargy [aut, cph], Sameh Abdulah [cre, cph], KAUST King Abdullah University of Science and Technology [fnd, cph], Brightskies [cph] Maintainer: Sameh Abdulah -Description: An R-wrapper for ExaGeoStatCPP: a parallel high performance unified framework for geostatistics on manycore systems. Its abbreviation stands for Exascale Geostatistics. The framework aims at optimizing the likelihood function for a given spatial data to provide an efficient way to predict missing observations. The framework targets many-core systems: clusters of CPUs and GPUs. +Description: An R-Interface for ExaGeoStatCPP: a parallel high performance unified framework for geostatistics on manycore systems. Its abbreviation stands for Exascale Geostatistics. The framework aims at optimizing the likelihood function for a given spatial data to provide an efficient way to predict missing observations. The framework targets many-core systems: clusters of CPUs and GPUs. License: GPL (>= 3) Imports: assertthat (>= 0.2.1), MASS, methods, Rcpp (>= 1.0.9) Depends: R (>= 3.5.0), assertthat (>= 0.2.1), MASS diff --git a/ExaGeoStatCPPConfig.cmake.in b/ExaGeoStatCPPConfig.cmake.in index 4e7ec663..410e4c61 100644 --- a/ExaGeoStatCPPConfig.cmake.in +++ b/ExaGeoStatCPPConfig.cmake.in @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/LICENSE b/LICENSE index 0808c82b..bcfd34e7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2017-2023, King Abdullah University of Science and Technology +Copyright (c) 2017-2024, King Abdullah University of Science and Technology All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 5b2b18be..ad84f4da 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -What is ExaGeoStat? -================ +# ExaGeoStat The **Exascale GeoStatistics** project (ExaGeoStat) is a parallel high-performance unified framework for computational geostatistics on many-core systems. The project aims to optimize the likelihood function for a given spatial data to @@ -9,18 +8,19 @@ from commodity x86 to GPU accelerator-based shared and distributed-memory system tackle computationally challenging scientific problems at large-scale while abstracting the hardware complexity through state-of-the-art high-performance linear algebra software libraries. - -What is ExaGeoStatCPP? -==================== +### ExaGeoStatCPP ExaGeoStatCPP is a C++ API for ExaGeoStat that aims to offer a user-friendly and efficient API for C++ developers, essentially maintaining traditional practices and embracing contemporary C++ elements like namespaces, templates, and exceptions to enhance functionality. +### ExaGeoStatR : R Interface of ExaGeoStat +R is a powerful and versatile tool for scientific computing, offering a wide range of statistical and graphical +techniques, strong community support, and the flexibility to integrate with other programming languages. +Its open-source nature and extensive package ecosystem make it an invaluable resource for researchers and data scientists. +Therefore, we decided to create ExaGeoStatR: An interface for functionalities provided by ExaGeoStatCPP to make use of R's various benefits. -Vision of ExaGeoStat/ExaGeoStatCPP -================================== +### Vision of ExaGeoStat/ExaGeoStatCPP The ExaGeoStat/ExaGeoStatCPP project is a collaboration between the KAUST Spatial Statistics group and the Extreme Computing Research -Center (ECRC). Its contribution lies not in a new algorithm nor a new dataset, -but in demonstrating the routine use of the larger datasets becoming available to geospatial +Center (ECRC). Lies not in a new algorithm nor a new dataset, but in demonstrating the routine use of the larger datasets becoming available to geospatial statisticians, thanks to the implementation of state-of-the-art statistical algorithms on High Performance Computing (HPC) hardware. @@ -53,147 +53,93 @@ This low-rank matrix approximation permits the exploitation of the data sparsity numerical accuracy. This further expands practical problem sizes for statisticians with modest computational resources. +## Installation -ExaGeoStatR : R wrapper of ExaGeoStat -===================================== - R is a powerful and versatile tool for scientific computing, offering a wide range of statistical and graphical - techniques, strong community support, and the flexibility to integrate with other programming languages. - Its open-source nature and extensive package ecosystem make it an invaluable resource for researchers and data scientists. - - Therefore, we decided to create ExaGeostatR: A wrapper for functionalities provided by ExaGeostat/ExaGeostatCPP to make use of R's various benefits. - More details about the exact usage are provided in the **```USER_MANUAL```**. - -Current Version of ExaGeoStatCPP: 1.0.0 -======================================= - -- ### Supported Operations: - -1. (Data Generation): Generating large geospatial synthetic datasets using dense, Diagonal Super-Tile (DST) and Tile Low-Rank (TLR) approximation techniques. -2. (Data Modeling): Modeling large geospatial datasets on dense, Diagonal Super-Tile (DST) and Tile Low-Rank (TLR) approximation techniques through the Maximum likelihood Estimation (MLE) operation. -3. (Data Prediction): Predicting missing measurements on given locations using dense, Diagonal Super-Tile (DST), and Tile Low-Rank (TLR) approximation techniques. -4. (MLOE/MMOM): Computing the Mean Loss of Efficiency (MLOE), Mean Misspecification of the Mean Square Error (MMOM), and Root mean square MOM (RMOM) to describe the prediction performance over the whole observation region. -5. (Fisher Information Matrix (FIM)): Quantifying the information content that a variable x carries about a parameter $\theta$ within a Gaussian distribution. - -- ### Supported Covariance Functions: - -1. Univariate Matérn (Gaussian/Stationary) -2. Univariate Matérn with Nugget (Gaussian/Stationary) -3. Flexible Bivariate Matérn (Gaussian/Stationary) -4. Parsimonious Bivariate Matérn (Gaussian/Stationary) -5. Parsimonious trivariate Matérn (Gaussian/Stationary) -6. Univariate Space/Time Matérn (Gaussian/Stationary) -7. Bivariate Space/Time Matérn (Gaussian/Stationary) -8. Tukey g-and-h Univariate Matérn (non-Gaussian/Stationary) -9. Tukey g-and-h Univariate Power Exponential (non-Gaussian/Stationary) - -- ### Programming models: - -1. MPI -2. Task-based programming models - -- ### External libraries: - -1. NLOPT [https://nlopt.readthedocs.io/en/latest/](https://nlopt.readthedocs.io/en/latest/) -2. GSL [https://www.gnu.org/software/gsl/](https://www.gnu.org/software/gsl/) -3. HWLOC [https://www.open-mpi.org/projects/hwloc/](https://www.open-mpi.org/projects/hwloc/) -4. StarPU dynamic runtime system [https://starpu.gitlabpages.inria.fr/](https://starpu.gitlabpages.inria.fr/) -5. HCORE [https://github.com/ecrc/hcore](https://github.com/ecrc/hcore) -6. HiCMA [https://github.com/ecrc/hicma](https://github.com/ecrc/hicma) -7. Stars-H [https://github.com/ecrc/stars-h](https://github.com/ecrc/stars-h) -8. Chameleon [https://gitlab.inria.fr/solverstack/chameleon](https://gitlab.inria.fr/solverstack/chameleon) - -Project Hierarchy --------------------- - -* **```cmake```** A directory contains essential CMake modules that facilitate the importation and location of required dependencies. -* **```docs```** A directory contains all the necessary documents. -* **```examples```** A directory contains a comprehensive collection of demo code that illustrates the framework's application and demonstrates its features and capabilities. -* **```inst```** A directory contains all the system's header files, mirroring the structure of the src directory. -* **```man```** A directory contains all the R functions documentation. -* **```scripts```** A directory contains benchmarking scripts. -* **```src```** A directory contains all the source files. -* **```tests```** A directory contains all the test files and follows the same structure as the src folder. -* **```clean_build.sh```** A script is designed to compile the software tests once all dependencies are installed, and it is set to build everything by default. -* **```CMakeLists.txt```** The top-level CMake file to configure the build system. -* **```configure```** A Script used to generate the building system inside a 'bin' directory. - -Installation -============ - -> Note: Installation requires at least **CMake of version 3.2**. to build ExaGeoStatCPP, +> Note: Installation requires at least **CMake of version 3.2**. to build ExaGeoStatCPP. +### C++ source code installation To install the `ExaGeoStat` project locally, run the following commands in your terminal: -1. Clone the project from the remote github repository into your local machine using the following command - - `git clone git@github.com:ecrc/exageostatcpp` for using SSH key or - - - `git clone https://github.com/ecrc/exageostatcpp` for using HTTPS +1. Clone the project from the remote gitHub repository into your local machine using the following command + ```bash + git clone https://github.com/ecrc/ExaGeoStatCPP.git + ``` 2. Change your current directory by getting into the `ExaGeoStatCPP` project directory - - `cd exageostatcpp` - + ```bash + cd ExaGeoStatCPP + ``` 3. Run `configure` script with the flag `-h` for help, to know the supported options and their corresponding flags. - - `./configure -h` - or check user manual. - + ```bash + ./configure -h + ``` 4. Run `clean_build.sh` script with the flag `-h` for help, to know the needed arguments to run with your specific options. - - `./clean_build.sh -h` - + ```bash + ./clean_build.sh -h + ``` 5. Export the installation paths of the dependencies to your `.bashrc` file, e.g. - - `export PKG_CONFIG_PATH=$PWD/installdir/_deps/DEPENDENCY_NAME/lib/pkgconfig:$PKG_CONFIG_PATH` + ```bash + export PKG_CONFIG_PATH=$PWD/installdir/_deps/DEPENDENCY_NAME/lib/pkgconfig:$PKG_CONFIG_PATH + ``` Now, you can use the pkg-config executable to collect compiler and linker flags for -EXAGEOSTATCPP. - -- ## ExaGeoStatR Setup -Run the following commands in your terminal: - -1. Install `R` package using the package manager - - `apt install r-base r-base-dev` - - > `r-base` is the package that provides the base R installation, including the R interpreter, basic R packages, and the R development tools. The `r-base-dev` package, on the other hand, is an additional package that provides development tools for building R packages from source. It includes compilers and other tools necessary for compiling R packages from source code. - -2. Open the R prompt window by simply running `R` command in the terminal - -3. Inside the prompt, we will install needed packages by running the following commads: - - `install.packages(Rcpp)` - - > Rcpp is a package that provides seamless integration between R and C++, allowing R users to write high-performance code in C++ and call it from R - - - `install.packages("assert")` - - > The assert package in R provides assertion functions, which are used to check assumptions in your code. - - -4. Return to the terminal and run the following command, make sure your current path is the ExaGeoStat project directory - - `R CMD INSTALL . --configure-args="-r -e"` - - > This command installs our ExaGeoStat code as a package to be able to run it using R. We pass `-r`to the configuration to enable usage of R - - -Using ExaGeoStatCPP -=================== -Please refer to **```USER_MANUAL```** for detailed instructions. -Please take a look at the end-to-end examples as a reference for using all the operations. - -Contribute -========== - -[Contribution Guidelines](CONTRIBUTING.md) - -References -========== +ExaGeoStatCPP. + +### R package installation +1. Open the R prompt window by simply running `R` command in the terminal, inside the prompt, we will install needed packages by running the following commands: + ```R + install.packages(Rcpp) + install.packages("assert") + ``` + +2. close the R prompt and return to the terminal. Run the following command, make sure your current path is the ExaGeoStat project directory + + ```commandline + R CMD INSTALL . --configure-args="-r" + ``` + +> For more detailed information on setting up ExaGeoStat with different configurations and enabling technologies such as CUDA, MPI, R, etc., please refer to the [User Manual](USER_MANUAL.md) + + +## Usage +#### C++ Example +```C++ +int main(int argc, char **argv) { + + // Create a new configurations object. + Configurations configurations; + // Initialize the arguments with the provided command line arguments + configurations.InitializeArguments(argc, argv); + // Initialize the ExaGeoStat Hardware + auto hardware = ExaGeoStatHardware(configurations.GetComputation(), configurations.GetCoresNumber(), configurations.GetGPUsNumbers()); + // Load data by either read from file or create synthetic data. + std::unique_ptr> data; + ExaGeoStat::ExaGeoStatLoadData(configurations, data); + // Modeling module. + ExaGeoStat::ExaGeoStatDataModeling(configurations, data); + // Prediction module + ExaGeoStat::ExaGeoStatPrediction(configurations, data); + + return 0; +} +``` +### R Example: +```R +hardware <- new(Hardware, computation, ncores, ngpus) +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) +estimated_theta <- model_data(data=exageostat_data, kernel=kernel, dts=dts, dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10) +predict_data(train_data=list(x, y, z_measurement), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) +``` + +> Please take a look at the end-to-end examples as a reference for using all the operations. + +## Contributing +Find detailed information on how to contribute to ExaGeoStatCPP [here](CONTRIBUTING.md) + +## References 1. Sameh Abdulah, Hatem Ltaief, Ying Sun, Marc G. Genton, and David E. Keyes. "ExaGeoStat: A high performance unified software for geostatistics on manycore systems." IEEE Transactions on Parallel and Distributed Systems 29, no. 12 ( @@ -228,13 +174,17 @@ References geostatistical modeling and prediction for extreme-scale environmental applications." In 2022 SC22: International Conference for High-Performance Computing, Networking, Storage and Analysis (SC), pp. 13-24. IEEE Computer Society, 2022. (ACM GORDON BELL PRIZE Finalist). - + 9. Sagnik Mondal, Sameh Abdulah, Hatem Ltaief, Ying Sun, Marc G. Genton, and David E. Keyes. "Tile low-rank approximations - of non-Gaussian space and space-time Tukey g-and-h random field likelihoods and predictions on large-scale systems." - Journal of Parallel and Distributed Computing 180 (2023): 104715. - + of non-Gaussian space and space-time Tukey g-and-h random field likelihoods and predictions on large-scale systems." + Journal of Parallel and Distributed Computing 180 (2023): 104715. + 10. Qinglei Cao, Sameh Abdulah, Hatem Ltaief, Marc G. Genton, David E. Keyes, and George Bosilca. "Reducing Data Motion and Energy Consumption of Geospatial Modeling Applications Using Automated Precision Conversion." In 2023 IEEE International Conference - on Cluster Computing (CLUSTER), IEEE, 2023. - -![ExaGeoStatCPP-handout.png](docs%2FExaGeoStatCPP-handout.png) \ No newline at end of file + on Cluster Computing (CLUSTER), IEEE, 2023. + +## License +[BSD 3-Clause](LICENSE) + +## Handout +![ExaGeoStatCPP-handout.png](docs/ExaGeoStatCPP-handout.png) \ No newline at end of file diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 87e03ff7..9ba2617f 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -1,33 +1,82 @@ -ExaGeoStatCPP User Manual -========================= +# ExaGeoStatCPP User Manual -# Content +## Content -1. [Configurations of the software](#configurations) -2. [Building ExaGeoStatCPP](#building) -3. [Supported Covariance kernels](#supported-covariance-functions-kernels-) +1. [ExaGeoStatCPP v1.1.0](#ExaGeoStatCPP) +2. [Configurations of the software](#configurations) +3. [Building ExaGeoStatCPP](#building) 4. [Arguments](#arguments) 5. [List of Descriptors](#list-of-descriptors) 6. [Supported operations](#supported-operations) -7. [Contributing](#contributing) +7. [Manuals](#Manuals) +8. [Contributing](#contributing) + +## ExaGeoStatCPP +> Current Version of ExaGeoStatCPP: 1.1.0 +### Supported Operations: +1. (Data Generation): Generating large geospatial synthetic datasets using dense, Diagonal Super-Tile (DST) and Tile Low-Rank (TLR) approximation techniques. +2. (Data Modeling): Modeling large geospatial datasets on dense, Diagonal Super-Tile (DST) and Tile Low-Rank (TLR) approximation techniques through the Maximum likelihood Estimation (MLE) operation. +3. (Data Prediction): Predicting missing measurements on given locations using dense, Diagonal Super-Tile (DST), and Tile Low-Rank (TLR) approximation techniques. +4. (MLOE/MMOM): Computing the Mean Loss of Efficiency (MLOE), Mean Misspecification of the Mean Square Error (MMOM), and Root mean square MOM (RMOM) to describe the prediction performance over the whole observation region. +5. (Fisher Information Matrix (FIM)): Quantifying the information content that a variable x carries about a parameter $\theta$ within a Gaussian distribution. + +### Supported Covariance Functions: +1. Univariate Matérn (Gaussian/Stationary) +2. Univariate Matérn with Nugget (Gaussian/Stationary) +3. Flexible Bivariate Matérn (Gaussian/Stationary) +4. Parsimonious Bivariate Matérn (Gaussian/Stationary) +5. Parsimonious trivariate Matérn (Gaussian/Stationary) +6. Univariate Space/Time Matérn (Gaussian/Stationary) +7. Bivariate Space/Time Matérn (Gaussian/Stationary) +8. Tukey g-and-h Univariate Matérn (non-Gaussian/Stationary) +9. Tukey g-and-h Univariate Power Exponential (non-Gaussian/Stationary) +> To add your kernel, please refer to [Contribution Guidelines](CONTRIBUTING.md) + +### Programming models: +1. MPI +2. Task-based programming models + +### External libraries: +1. NLOPT [https://nlopt.readthedocs.io/en/latest/](https://nlopt.readthedocs.io/en/latest/) +2. GSL [https://www.gnu.org/software/gsl/](https://www.gnu.org/software/gsl/) +3. HWLOC [https://www.open-mpi.org/projects/hwloc/](https://www.open-mpi.org/projects/hwloc/) +4. StarPU dynamic runtime system [https://starpu.gitlabpages.inria.fr/](https://starpu.gitlabpages.inria.fr/) +5. HCORE [https://github.com/ecrc/hcore](https://github.com/ecrc/hcore) +6. HiCMA [https://github.com/ecrc/hicma](https://github.com/ecrc/hicma) +7. Stars-H [https://github.com/ecrc/stars-h](https://github.com/ecrc/stars-h) +8. Chameleon [https://gitlab.inria.fr/solverstack/chameleon](https://gitlab.inria.fr/solverstack/chameleon) + +### Project Hierarchy + +* **```cmake```** A directory contains essential CMake modules that facilitate the importation and location of required dependencies. +* **```docs```** A directory contains all the necessary documents. +* **```examples```** A directory contains a comprehensive collection of demo code that illustrates the framework's application and demonstrates its features and capabilities. +* **```inst```** A directory contains all the system's header files, mirroring the structure of the src directory. +* **```man```** A directory contains all the R functions documentation. +* **```scripts```** A directory contains benchmarking scripts. +* **```src```** A directory contains all the source files. +* **```tests```** A directory contains all the test files and follows the same structure as the src folder. +* **```clean_build.sh```** A script is designed to compile the software tests once all dependencies are installed, and it is set to build everything by default. +* **```CMakeLists.txt```** The top-level CMake file to configure the build system. +* **```configure```** A Script used to generate the building system inside a 'bin' directory. + ## Configurations * Run the help of `configure` to know the needed arguments for your specific options. + ``` bash + ./configure -h + ``` -```commandline -./configure -h -``` - -* To Enable support of HiCMA, add `-H` disabled by default. +* To enable R interface, add `-r` disabled by default. +* To enable support of HiCMA, add `-H` disabled by default. * To enable examples, add `-e` enabled by default. * To enable tests, add `-t` disabled by default. * To enable heavy tests, add `-T` disabled by default. * To enable CUDA, add `-c` disabled by default. * To enable MPI, add `-m` disabled by default. * To enable verbose output, add `-v` disabled by default. -* To change the installation path of the dependencies, use `-i ` project_path/installdir/_deps/ by default on Unix systems. -* To enable manually passing mkl as BLA vendor, add `--use-mkl` MKL by default. +* To change the installation path of the dependencies, use `-i ` the default is project_path/installdir/_deps/ on Unix systems. * To enable packaging system for distribution, add `-p` disabled by default. * To enable showing code warnings, add `-w` disabled by default. * To manually set mkl as blas vendor, add `--use-mkl`. MKL is required as blas vendor and it's automatically detected but in some environments it need to be manually set. @@ -36,51 +85,20 @@ ExaGeoStatCPP User Manual * Run the help of `clean_build.sh` to know additional argument options. -```commandline -./clean_build.sh -h -``` + ```bash + ./clean_build.sh -h + ``` * Run clean_build.sh to build the project. -```commandline -./clean_build.sh -``` -* To build and install the project, Run the following command. -```commandline -./clean_build.sh -i -``` -* To build and enable verbose printing, Run the following command. -```commandline -./clean_build.sh -v -``` -* To enable building with a specific number of threads, run the following command. -```commandline -./clean_build.sh -j -``` + ```bash + ./clean_build.sh + ``` +* To enable the installation of the project, add `-i` disabled by default. +* To enable verbose printing, add `-v` disabled by default. +* To enable building with a specific number of threads, add `-j ` running with maximum number of threads by default. -Supported Covariance Functions/ Kernels: -====================== - -1. univariate_matern_stationary -2. univariate_exp_non_gaussian -3. univariate_matern_dbeta -4. univariate_matern_ddbeta_beta -5. univariate_matern_ddbeta_nu -6. univariate_matern_ddnu_nu -7. univariate_matern_ddsigma_square -8. univariate_matern_ddsigma_square_beta -9. univariate_matern_ddsigma_square_nu -10. univariate_matern_dnu -11. univariate_matern_dsigma_square -12. univariate_matern_non_gaussian -13. univariate_matern_nuggets_stationary -14. univariate_spacetime_matern_stationary -15. bivariate_matern_flexible -16. bivariate_matern_parsimonious -17. bivariate_spacetime_matern_stationary -18. trivariate_matern_parsimonious - -* To add your kernel, please refer to [Contribution Guidelines](CONTRIBUTING.md) -## Arguments +## Arguments +These are the arguments that you can specify when running any C++ example. * {Mandatory} To set the problem size (N) --N= @@ -200,8 +218,6 @@ Supported Covariance Functions/ Kernels: 8. DESCRIPTOR_C12UV : HiCMA descCUV descriptor 9. DESCRIPTOR_C22UV : HiCMA descCUV descriptor - - ## Supported Operations ### Provide Arguments @@ -243,7 +259,7 @@ The subsequent arguments are as follows: - `number of gpus`: Specifies the number of GPUs to be used for the solver. -##### *ExaGeoStat R wrapper* +##### *ExaGeoStat R Interface* ```R hardware <- new(Hardware, computation, number of cores, number of gpus); hardware$finalize_hardware() @@ -252,7 +268,7 @@ First arguement represents the name of the R class that wrapps its correponding ### Types of Data -`ExaGeoStatCPP` can be used with 2 types of data: +ExaGeoStatCPP can be used with 2 types of data: - Synthetic data i.e. generated by the software according to the user arguments. - Real data e.g. data from satellite imagery or weather sensors. Real data can be used to train the software to predict the values of new data better. @@ -271,53 +287,52 @@ ExaGeoStat::ExaGeoStatLoadData(configurations, data); #### Using Real Data -Here we use already existing data by providing the path to it: +Here we use existing data by providing the path to it: - The Data Path must be passed to Configuration -``` -data_path <- -``` + ``` + data_path <- + ``` And then using the following code: ```R exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension, data_path=data_path) - ``` ### Location Getters -ExaGeoStat supports locations data of dimension upto 3D, and therefore we have getters for X, Y and Z coordinates. +ExaGeoStat support 2D and 3D spatial locations, and therefore we have getters for X, Y and Z coordinates. #### X-coordinate Getter ```c++ double *locations_x = exageostat_data->GetLocations()->GetLocationX(); ``` -##### *ExaGeoStat R wrapper* +##### *ExaGeoStat R Interface* ```R locations_x <- get_locationsX(data=exageostat_data) ``` The subsequent arguments are as follows: -- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data.. +- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data. #### Y-coordinate Getter ```c++ double *locations_y = exageostat_data->GetLocations()->GetLocationY(); ``` -##### *ExaGeoStat R wrapper* +##### *ExaGeoStat R Interface* ```R locations_y <- get_locationsY(data=exageostat_data) ``` The subsequent arguments are as follows: -- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data.. +- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data. #### Z-coordinate Getter ```c++ double *locations_z = exageostat_data->GetLocations()->GetLocationZ(); ``` -##### *ExaGeoStat R wrapper* +##### *ExaGeoStat R Interface* ```R locations_x <- get_locationsZ(data=exageostat_data) ``` @@ -339,7 +354,7 @@ The used variables are as follows: - `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data. - `desc_Z_values`: pointer to descriptor matrix. -##### *ExaGeoStat R wrapper* +##### *ExaGeoStat R Interface* ```R desc_Z_values <- get_Z_measurement_vector(data=exageostat_data, type="chameleon") ``` @@ -357,7 +372,7 @@ ExaGeoStat::ExaGeoStatDataModeling(hardware, configurations, data, z_mat ``` -##### *ExaGeoStat R wrapper* +##### *ExaGeoStat R Interface* ```R estimated_theta <- model_data(matrix=z_value, x=locations_x, y=locations_y, kernel=kernel, dts=dts, dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10, computation=computation, band=1) ``` @@ -373,7 +388,7 @@ ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); ``` -##### *ExaGeoStat R wrapper* +##### *ExaGeoStat R Interface* ```R predict_data(train_data=list(locations_x, locations_y, locations_z, z_value), test_data=list(test_x, test_y, test_z), kernel=kernel, dts=dts, estimated_theta=estimated_theta) ``` @@ -391,7 +406,7 @@ ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); ``` -##### *ExaGeoStat R wrapper* +##### *ExaGeoStat R Interface* ```R fisher_matrix <- fisher(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) ``` @@ -410,7 +425,7 @@ ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); ``` -##### *ExaGeoStat R wrapper* +##### *ExaGeoStat R Interface* ```R result_mloe_mmom = mloe_mmom(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta, true_theta=true_theta) ``` @@ -429,10 +444,14 @@ ExaGeoStat::ExaGeoStatPrediction(configurations, data, z_matrix); ``` -##### *ExaGeoStat R wrapper* +##### *ExaGeoStat R Interface* ```R idw_error = idw(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta, test_measurements=test_measurements) ``` + +## Manuals +- Find a detailed Manual for R functions in [ExaGeoStatCPP-R-Interface-Manual](docs/ExaGeoStat-R-Interface-Manual.pdf) +- Find a detailed Manual for C++ functions in [ExaGeoStatC-CPP-Manual](docs/ExaGeoStat-CPP-Manual.pdf) + ## Contributing [Contribution Guidelines](CONTRIBUTING.md) - - \ No newline at end of file diff --git a/clean_build.sh b/clean_build.sh index 7f554073..3cd86582 100755 --- a/clean_build.sh +++ b/clean_build.sh @@ -1,5 +1,5 @@ #! /bin/sh -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/FindR.cmake b/cmake/FindR.cmake index a341c146..94806380 100644 --- a/cmake/FindR.cmake +++ b/cmake/FindR.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportBLAS.cmake b/cmake/ImportBLAS.cmake index 9bc2b502..8b2bec5d 100644 --- a/cmake/ImportBLAS.cmake +++ b/cmake/ImportBLAS.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportBLASPP.cmake b/cmake/ImportBLASPP.cmake index 6acea7c5..480b2e5c 100644 --- a/cmake/ImportBLASPP.cmake +++ b/cmake/ImportBLASPP.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportCatch2.cmake b/cmake/ImportCatch2.cmake index 0b6a7f9f..263a54bf 100644 --- a/cmake/ImportCatch2.cmake +++ b/cmake/ImportCatch2.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportChameleon.cmake b/cmake/ImportChameleon.cmake index b72df9ba..650db197 100644 --- a/cmake/ImportChameleon.cmake +++ b/cmake/ImportChameleon.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportGSL.cmake b/cmake/ImportGSL.cmake index 016b0511..9a159744 100644 --- a/cmake/ImportGSL.cmake +++ b/cmake/ImportGSL.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportHCore.cmake b/cmake/ImportHCore.cmake index b5d51aad..494f26e5 100644 --- a/cmake/ImportHCore.cmake +++ b/cmake/ImportHCore.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportHiCMA.cmake b/cmake/ImportHiCMA.cmake index 532de60d..bb879d66 100644 --- a/cmake/ImportHiCMA.cmake +++ b/cmake/ImportHiCMA.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportHwloc.cmake b/cmake/ImportHwloc.cmake index 04be165b..5f632725 100644 --- a/cmake/ImportHwloc.cmake +++ b/cmake/ImportHwloc.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportLapack.cmake b/cmake/ImportLapack.cmake index bb5517a4..da0ab62b 100644 --- a/cmake/ImportLapack.cmake +++ b/cmake/ImportLapack.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportNLOPT.cmake b/cmake/ImportNLOPT.cmake index f82c786b..b3ce08c6 100644 --- a/cmake/ImportNLOPT.cmake +++ b/cmake/ImportNLOPT.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportStarPu.cmake b/cmake/ImportStarPu.cmake index f51f0a42..523ebc06 100644 --- a/cmake/ImportStarPu.cmake +++ b/cmake/ImportStarPu.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/ImportStarsH.cmake b/cmake/ImportStarsH.cmake index 35cbeab7..0fd9cde5 100644 --- a/cmake/ImportStarsH.cmake +++ b/cmake/ImportStarsH.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/macros/BuildDependency.cmake b/cmake/macros/BuildDependency.cmake index 483dc05d..50f5c125 100644 --- a/cmake/macros/BuildDependency.cmake +++ b/cmake/macros/BuildDependency.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/macros/ImportDependency.cmake b/cmake/macros/ImportDependency.cmake index 806da212..1e6e71b0 100644 --- a/cmake/macros/ImportDependency.cmake +++ b/cmake/macros/ImportDependency.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/toolchains/CudaToolchain.cmake b/cmake/toolchains/CudaToolchain.cmake index a84a2666..89721ad9 100644 --- a/cmake/toolchains/CudaToolchain.cmake +++ b/cmake/toolchains/CudaToolchain.cmake @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/cmake/toolchains/GccToolchain.cmake b/cmake/toolchains/GccToolchain.cmake index 3fda7f79..82864184 100644 --- a/cmake/toolchains/GccToolchain.cmake +++ b/cmake/toolchains/GccToolchain.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/configure b/configure index 772a9ed0..5e6a92f2 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). @@ -161,6 +161,7 @@ while getopts ":tevhHi:cmpTwr" opt; do printf "%20s %s\n" "-v :" "to enable verbose printings." printf "%20s %s\n" "-p :" "to enable a packaging system for distribution." printf "%20s %s\n" "-w :" "to enable showing warnings." + printf "%20s %s\n" "-r :" "to enable R support" printf "%20s %s\n" "-h :" "Help." echo "" exit 1 diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index ca8e0208..28885b94 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/docs/ExaGeoStat-CPP-Manual.pdf b/docs/ExaGeoStat-CPP-Manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8c627d6c583e33372468e762151aac4b895a61fd GIT binary patch literal 1075902 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

lB~Z`1e&BQo4UVbY8W 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%0A0ib+caU=3iEVV+0iZZiQ?> z*6NtGm$XZX(_*TS5ZF!^uudP%Q$e^JmbQ?XXT^v*=atU26J4@2uTcY63ujCcVnwK3 zP4|XSRG6vdO~Gbi*F>dSC7mT-rJ+!gAJn(2h=>1v5MutPcK?6vq5ofzH1z+MG?amr z{eKKr7}ngh!D2=9&aK@$ynza>B8Vi?1F@r-p^2jkV1v4<(MP+QBojD7*C)E>@a-+t zNUC}xn2z#ffD>_ztr#CPST2`b#80#*DKZeCn-@LAwozn3Cd}!E zXfN}k3vLX@Kja>b6f@%bD~5=P3nWC2h8ww~cZK{cYv9!{6`z1#I*3?8=+Z_W!e%JI zB?q0B+>K9ej*t$09M+x&!wC?H1?|~Ch6TfT5>6H3Odg-8FOCMk$9Aa|rOZz17qR>i zwhFv5;*Y{14Z&fqhXf)Si@<;`86F3v?uTj{azL*aWEY+WMG0Aa)^T5=SoTplnP#wn z#TivqAz8txZLK1Fo;jxh{U+?h4oGXHoD_!u?j*kkp}#F35T8d!gn^*VkSFHigFuW< zF8rG${h($OM1XiXrsfg>J20L}L;(y!Zra`7(jL}cH+F^1g!?R_U*eZ}fa0ge@X2+} zYDeB-(7>mOV0fGah=rLIxdCXEC&j6jKwf0aPZ%uhOI{*$CX&}dATJscwFjduE^fi% zr-0}Oc~xn&7Tnh-u@a4BDsfK{MgB+c&)&cYR=I=SX}x9uDb?U`uv7|Wo&AAxr-A>3 z*Zg+BkN~V}1rWY%musD{yyp{+V>m_;>2mg&uDEHSwZ~O{Py92@7{Ne;o$3=zci{70 z%oFSf@`}8We7_sL-2x|et=l)H2!5$1l;n_+1@rnjww2U?lj^|1dgJhe<|DEo};KtZHVaHLs3z4bS(} zAzRjJE$i(RWiSM{rUU?L*Yq*7Nu?$6T8pHt9GVg7htQ z3axF&<@tN)SjW#K@9ODq_2#1o_fMGb<}ASU~hUs&1D_-lL*X_>9LPN>^fCvN} zv>Slmp!Gj0Le@_L(hZO~4$+jeZ;3F= zu5o)vYHgyu8>4DLeaLAwdGaUaVWjuoxh%CVtMdKo9X9i9$NXsm1)q?pLC1~OEcNuU zFk*%z+G{Q?^h7{PkGvPI%IERr+Ju{zA%?|NngWswKCUBNc%=om;rZh0%7oLt55Xl# z&pJaJ#ESTflupyd7&l0;yN1__;v^Fn`#?G*-#za^OdXhglKwjK#>Cr)yZ54vsL~w= zwK57pt6}WvrQe06M%3!+1R`f5j0xwxo3viTmCgswo)+cZxvPx}EZolEl4+Y5#ER%| znY)#fXZ@HhMC9QxLLNTSW0*V-@*8DSh!T|#$=^@trpU)~%l|LgcvUL3TwQ5u^45hga^l;>T7|DJCc-tIxv&P|LA--AL+C_nCaq1a9@#jYBF zPR9B7m+gyJG*@R*iDi#4lEJ7zQlDKaH=#U`nWUyJvnNEuft)>m3zM;ZZMIA{ysVnQed2OT!B0#Z+P3t zoI>6;!8q>DloMgC`DLQC6LAuCgZKCo+*(c2Q*0yWr{P5Sqs=+3r_Q-d`*HJx&ypr+ zMJsNFDQ+bwj)YWN=H^HPN#K7af@TsS3+XT+Q8S(*2+36@nE>Z`-i=5u3gUQK#jBMa zvM^wCgasmrSRjvtga*@dBQz~&ZH$g(i@lgn4FbbIx}WUm&1y6Z%wJ4Sh)t=F6*B9Z~QD%(2XJq!GT%q+Vv~yfnvQ#_)TDjtjQ1cEG5I`giFe_*D{T zs`?Q0Nps@PTgN-b7^4R#L9qs@Pny{I4MNFB@OTkEF^fLS{_iVjmKSU7Hfr*!o;U*7 zA2w$Juj!7(?faLmJ>`I6VMJxGQhte8h6 zH~D3v_O<2dX5dnvaPuL8^OIzOiE)Gv5WH^~AxoiLV5f#oK3Vlnb0`FX>7_}~QPNRK zsKJFPt$Htmd#-ywF$ZZXAfL1hB@*lHecIy9m?lAR6a3klC3-o+z3`*uw`Ia5c5M76 zkl*H+Sq*C7JVj@Ds!0X_6y;txbB5A#-^ZDUEyB#eV5Wqe2-4K#<|R3=w`9HL-xT-< zM=_!)4H$C9PK8hmH4dxQVJAz>54%B1;cVpMgqeR*N-P))s@UjGoP=Z;x~c`4uT|tN zaOL|}IUXY99A=<;%@9giwDcHYNf1jXb1HmedtgELLX#iM!pb|Q?%EeDzxeqROs7@5 zmKT8|bUte&&@z5_!G57&w2^L*U1=3?CR&cREP1ijUuhc9*?&U|WW5xQ@Z~vF_@?AH0UoxMAK7Vqjc9dvEdg}5@7E?T2eBKbStut;$}@eD0qr@`fM|N4x2 ziM`O{@b1LqKM63c4iOnyp%oP1kPrU6JLuU;&Q4!ru>9bC3fW#NHUQa3)7%hY>Vtfw zo1m`CJy9BLh^NSl`yaX{5SwVau|i{PK~2)o%gf(shfzX^xmwp=-P@r;G#l=1kLo|^ z=URmTi0>ByxO*O*+yRuAV*X8NIuvv8q_E{!Wz!ySuFPe|WBCvRd`@4Ph~`lULRI9! z_8x-ZQ@=SU95Ile%gu`y*;owi%)1j%fT_ig7A1pUBnNhyQ$*VL_FFL-CFk)b`|P)B z2r->j=110totE2b|pM89D>hf&1i1yWQmP$bS3Dzf$!2g;a<1z#6$F zsT`G1cS(Rr=zr}TcYt17-lp>-rlNS%X8Xd2`t_gPWqiO@^Ayebbs}~mYjykx+ho5P zyms)MxW{j06&rYTX!TJE@ex1)nEO@E7ZtmS2y^&z3aolb?k3&-)CIuCX(2tk(N!Rr zU7Jm_J^qd}!E@}D8@KF(JG?E#@4P|Wk^0RmphWr~0@PWU{v*bh{eMmolC|r&*@iH> z`>ksCH5YF}fDGX4$>A-QZQ_C2YJPCJrF~NZEEzT}f~XwN!TGxC${&DCCO`yncB1n^ zl%D_6iO0L84KS&9J3qLyS-JYE>icDx<^d^DIQDwO>q##nj_htPAurVRnfz)l68Jv* zy6e`1JD`5oc(J#;9)il*a`Xrqow#n@!)5i>Vq~0RJAAHcdsF-4DZJ-&6~AJUkQW5D zOA*a9vq*^`UJKuIx+k*ER3zY{vk4<|2Gu+l%7fd?U)kNQbIK!n8rXbK({B$QD` z0})nY$8SbNBX&S2(am-ze>M92JjUc2q8IQLw;?g&?*Io8BQLgCL-r}zvyMIE&tD%a zmbEbfOeX3cp@=52l&re6-eL0B&TT^V`IVuT7nf)>G@w>)cRCI%3=xX<)9LLFBF=1r z&`uu{kseJRUK@lygCHRd&xEvGRO+ws?hG(jpr{xB;Cf1}uoVX1^b8EQMFnQEm9YGe z`oc32oL9Qya{tHPX?v{cd(V%d`%zO`q8$sszR~X|0HkC*VUP?9QIH+_qJEq-eo!L; zFTLNKi~?Lf#e_{`fzc*$uOac4Vq5U~BtVdh%^AMZc3UnI-}U89&``3Ij0oTG@I{|2 z+tBJ)ot7!(Ov`HRMd2!Di?@NXJJ-M@ae(7EKz1Vd3MrJ-P!0aVxQMiAQix&rR+3B< zf(-OoQ_|96c|?P2dl<#6Bf-JkoYz@<;+ZMr`aq!Hyzjz_kj6Yo?^-r80kwon@w0yLyrd zU~)=lXP~&fD^M{=Df(8l0Ny6CCV4!M6jdm(M85y!vRxW5Qk)< zx!9~M%37Xr;iSu~D8{NT&jHG~6K@+lei~rOgx?MUI=n1Inyx8v z$*?Sfr4dgM{(5UJu&hHL)>san5jno5$4Ck2;R;iVQBz}!_CUC00I%#I6u zi9Kcc{QcPSheRZ@x#C!lyBffv>k)f`?l&+)=DsF+zN#pIHbKJ`lHiK1&2GZcRqkMI zJuf-k#S4_hmB_`VLnw)X{#Oh-#Hs2JoiTh#UAg?%zu&}I7SXUxO}P%>?=s*4q`d$7 z2oFEmV$8@->$tv*ck~*&5DRBp+a>!f^%>lK{4N?6SUoBY9>hiH`v#|*@&R)%-E(l6 z&wYOmeBB62QfC?E)PVo~v@fD-FwF=}_9sf9D7hd8UO z)?(7DccP{0D46A$Hn#m+piKtb8JAh2a)*sn>))`^vJVJ<p2P!y{?aAiuCzW{z$9 zq4TRuZo^Sk-hy7m^`X@&jp>NRP|K^CUmh2Uwv78A>lSi?SpX3xnO~NXkF*qh47-dy zwQojRJ=j2C9ef_$g)!6+lq6berEV_K|fvR%!D6<+n>v>z7gZ_u=K_Sh4&w|v9=WB zbU_xWh9q$vd%w`(Lfyzu#1moT;eD~v%;{agm*Pseb1P29*7&|o=4}gF>-<_5o`@l8 zztoSav6MRFnMAE;Nf)FJvv-z{=I`2Vw4x-4%IOCIYQ}ZGUH+xb?sDofQ{9%4>MzWk zLzy*9xhUEO4MvRkKi~oQ$ z{BV=w^vvK8FB+3jcKPG2YGT>04P(H=27SPstZsI^klzgUQ{{ysHP2dA89a}nvPac#vHVtSdDLAwpUyxmr=jmX;|tl zMt~|PF_E%dTUMoaB&-Qy&erKW5%H1BgWEFM(J%hBjP;lDUzA4ohNyW7cCqZ@I^k|h zx5_faUXTH}FOz8{Y97a3UxbCxUGsdU1(kkv&+UPn^X}i$%G+7>cO_X7nb8du^HL&j z8w~^DbX8}^$(<@%XaDVLVc9?O{C~M(lQen@MfYCmVYPPV9U0_hp@yL+;b`e;iGl=B zVn} zV^PxZ_V+H;eTX~|0sy(Pt(Lq>kcm;%k!3*Xz??Q9E%ud&r*|qX)|wmzB15MIs@Yh4Uk`Ld`7%N}Nkd{J zD&hPAs^UP$U?Ex`zgT;K1xoBO5zv^!L23WJF)iP<%rmJGT6bD%^}ZBZt&_FW>U+?qG_hW!i-%7@tOGYllj|CEx;5LV4;W)EeG+~2*5 z_q3MpRbQfL_!$a~O**kw^q8F=Kj?GMAp0RQLGj z`sDnx)zfO2ANv}9^9LRupmVkvv&SnAr(@geLVsAW|D#%q7JJQT+$jSYZFXznuIO5u zk}krtlDsyBX#24I4Z)4$^*N?`RU;AY^hV0-6+77`mkfEpt|B7dmB+_9F9~29+F$dM zY?<4qXZ*xRHi{#y-!HEG9W|=|hqHGK)+K7X1dr`|Y}>YN++*9e@3C#$wr$(CZO`fH zp6KcLzLpwI2|u=g&}8GELQUcQmBvVv!0tGn|Z#slLkEcN&6pCH~J5=1T~ zFgefRiq2GB9iL^Jq^Pgjxm954TFk)uD|l3UY+kPE@;XCEFS=(U$xOuw6n28qs2IPI?$Z z)i<~}s~WLOu7`ITfvwXKZX~w7lr6)(Xj9}uBCVf7ft zj;M9UTkdvT%BO%eFz^a5Nxd(t&$Z}gp|~R#3LiR{m=T(RSJex%B6E`jdLHjUz(g{& z!n|SnCvh0Rt1c?F!57-jIU>m2oPjkC+S1ZBB}Nj10rMjT`)D#!qI?l2%(?=C?QZ2E6o6+6>vC)O}p`WVXobQZTs(kd(!JL$5$ zQOp#*gYOF(LaR`>=@$xZ1P*a~>@M_N7Kkq!+8f(P|yG`>Qp*!`CUy&%5^owbw}q(ZA`a zCFm#YBM#E&f_N8-s8B7;?+)P&2w(0MKp-g)4}X!y|00%oNgD+b+aU#ml8ZPt{hU6uRTLAoAS za1{zY+-8iIc|J-jj_+d61d!Dp<&dZ`jxui1_SKh2Qf1Q=X->=n8$CDlN}5dgdvD0p zGfoPNDio1h#0(D3Qfsf7+z6p(Uk=j>;`d{rlZ1@Xqu<}IHtwJZ<>cYRaOthe72ymN zE9M;}MDv>1?gB)7P1ZK7G8^JWh#02^iG0|YS$eRs9k_GE;pIOVjkt{M?;gzJbBG7$ zEjY#;0cxK;3Jz}4Iz20ymo$_$HboT9*8;y8$q`*zu|~&AXt?&beJz+5z>x3A z1wCWja*KZ8dTXi7b)q~bHVPE3tDRmB!+~yfX{hdKa%}rj5QK1b_LW^c7%;B4XA3dhySu<9?n3?JWH(nUw+FAJi^)x6jz)K)w*aRSCRFlgSyoX_|RAxRa!#T zPEkm&vsv};Nl0UZXTvga?SlxUUm1V^>DP?~wti>7w^&g~?*KXsr#l;LbHLB=QgZl6)#qDxOc25Q{=O^3TMKD3O0#SHz>- zs2zW14(Ykn=vfzNB5B-dIJUZad?E-*q*$v;7#zpOJmi&i6>`eMD;b}0xi7mq-Zt)! zH(I#w1(6{Ix{gfgddZRdp$YLI_i0wPYnXH=eKmbf6vp+DxmC12LpIB;$W$%5Zv*UH za;o7plMWeU7KGt@+#5RIZeacPR*8}p>4o!VI+vc0S9WS!f<1TK(dWn?v%}*wW9-A( z3+y`YcsD;Uz*gcc5XqtF=fokO5RLIQ5dx(uE11Xb-vsrzC+?rNw3BZR4*RrTMGxMe zt^cAS*%PPgVv^h#(h~yz(MhX=4T9!!6MzV3XNy7B=q^>xzMdiy`0E)sKy9EA{u3cY zKAKNREv6-+@i$tnBt#DD2tk;_0FWxA%jYT{8JTsRV7g35>o4%31Kq))&hb2lBZ!Wd za6paHNhW8~k;gH1z8b)e6vypO>K`Vs?wA9#j@tr+H)OXnCUk4Qz@CzgEdtfFH|__q zkF4&f2C*i#5&L_0**of`60c^K925}ipVY})Mc6P^w{!IxH^T(V`mchO3lBOX>MerEe zN4Hsr_6%rtUlh}L2ZPvH2zK&0dfwk{$ZRinmlAj-!>v2IeE_6TR?C}m9+L!gnK}f_ zJcvHx%k&)_YVuJMd>K3YBIyHN>x1`yI@>;nv}RFPU{Rhc7Ooj%dQR;@ysY~EVMTgX ze>H!*mk>pk-YG8x5MFMSy`OBFGPSiNh>TJ*5mxTF1|b4wK}dOJ7;)St!n2g5IsnCSC>N=92w0Bpkv^ zL9p!G7j|eF%*mgoxw7g^f$iiZKpbHJO#fxns;dfg+d2kRd7@?+^UQmKE}1Ww#E+? zj^FOW$5q9}u2?c&Ua!qK==SxZ?l=diJ&Yp>Hs02NsVM8+PMG0v9?Isd3vKaM^GbWP7Sryf4^hTQ=S(S zLJss=-PJ(}7WNv`INt6H3*&+>hJEpzI%x{6*}aK`rNrKULs z(4k7|Z2Uo_IK%fY&UAeAWjjK}IE=*f;rFAI&Pa8&%a|)b4>%`nOrfDi^6Z%EqVpm1 zR|n1KX_Vg1_BmvOj{^ecavZ^hXTvIv%^9hI^^au8FX)2^rG?Rey*C%0e$Qf}r(I^O zXFc!=^fnA=mrw(jJHHd)6g6tqqIKYm=(v%}hW^fB8OlGm=*Fcb+Gf1A`Nd4aYOiBp z2$Za~D19ME$#JZ<{sd9U#pjj@D+5{ThQhw@_4qM9aoKh6=k^CMQgKU<1OOAR)< zG=L=Z&r2Ag?-uFRm4uB~%vXNc zc}-1UX8M*p4+GQQkzm=jaB2igahXhK_Rs$xTLf@{MPeZ(ns?2!OU$!F zT)dXDp?3wX4{p`dJ{D-nk=W<^P@AkkiiKlRXo5Aqt91e>1bB*dOv2~A^aH@$tr}D% z-RoA(3hyj=f8^h1q4!1IrKZ$HbzL&uh(TJy=dzlJ|J(az(0-{it=LfA;Q75bA$^t_ zxvWxcMJ0R@vVkXYfHIA0=U*=a1qaGyRDt?#U9sA)so(O>fu0a{1h}cW0JDsf;W;Pg z?T3sQ^5z2{GSJ{hd)3t?g4L)X&)-?4*jTua6I2jqvyz-nE-ppi`gClhwgI|lZnU#w ztFa#s7Rl@Glz%cT75UOi8)rRO2yJI7w?2hQcvw@CM9hDRst<|Smwdcqt^UbiKeEWTu!C&8{wRiMs9!0qlozhE z-5(^}o)}B)lG(Az_VMX%;E6H&^Q^?mik?L$yLn>(t487pU6(sa8&

  • 4s5|bPT;V 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

    2>3e^z+H< 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

    NbGmEI{lW;4Yz%^@GQtqk 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%0AH@1&~Rtrz@uk8R>VE+j#{MSDH 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 literal 0 HcmV?d00001 diff --git a/docs/ExaGeoStat-R-Interface-Manual.pdf b/docs/ExaGeoStat-R-Interface-Manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..12458e1c4f05ab17ae968bab6ea27aa8801f73cf GIT binary patch literal 119253 zcmb5VV{mV6*X0}Aww>&lJ9hrDZQHhO+qRP(+qS)9+d22!-KV>&-n;5NXH~7L>(i?B zW&XyPW6q0QUPSB{6FnOY`P|a*8Voxd5hIbEp(P9t4-A8}iLIHlIS~^JCo9qacwiXB zEUcYP9EljjtPPw^L`;nAj7?zp_+Xrz9Zd{uVB9yZ)NSlG*^$0?^a|{n<|8PcEYY_GG1mp@wAv1<&^%l}yMD_#jk_Qz{W7(7QZ|U>Ma&>Q#(K-ypMZG&a_eu&= zY^#(fC;ZX9biP?#uoZ(?fPp!d6KS8s*N8(;;0!VM83m2;7~Xy({)0fzfP@=HHh+{v z5nbaM2My`*rMT(9VBp+ltkM3{S~Rajpncs1SizYvjv9CatHJo+$&T3C{qW^6GUAGM zq08W8&j+g8Q<`m6EgrXG)=}DLkyZtlsNExKP2=N}X|5h44=UT^U9&Casi~me&2%)K zKqSf&znH^N*ujrNgU1F05{nm@MSUvO&$Eu}&;2Pu66e})B6bDiV?|1g6A-xu3dc&OO!|qFqUGi0=drRCFWB6j$MWOB9Llaq_3Wa4 z3&XGzx)0N$l2&nloF=Z~9CH6!)wAb1(Q0U&EUK#BvG4jLO?zkm0tti>OMeX8mGWjO z-iq2Y_IC|dTu&xH!t!!HVRI;Ok_y@=I&^M8RgtvMFd|-&`kuYqJr+aI6?pvYHv_w< z*#h${K_E}LL3=Us>%yqGFpZ5?swqh z4q`OmITOQc`d>8471zlaTB7xq7HE5FYW0Id&KxUg2XH-XM25v`qY9sNa>`!%xa-HpEGd69Hzft8 z6N;j(wklwcDrpE>POGLmq?^?mSj}(=WO7MdwYqMeXVEgG9Y$4YmNQhfsTF+KPFQ#R zYG+whS?3ql?ysJqZg2y2Mn!-_Z8`$Tf_�xG7ICE$fc62T1RyzfPq~Nz@JmXbTr> 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 literal 0 HcmV?d00001 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7bc70451..450c6d91 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/configurations/CMakeLists.txt b/examples/configurations/CMakeLists.txt index c1c09aa1..e0ea9f99 100644 --- a/examples/configurations/CMakeLists.txt +++ b/examples/configurations/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/configurations/RunningWithDifferentConfigurations.cpp b/examples/configurations/RunningWithDifferentConfigurations.cpp index 4e85cc4b..e5998532 100644 --- a/examples/configurations/RunningWithDifferentConfigurations.cpp +++ b/examples/configurations/RunningWithDifferentConfigurations.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/configurations/SetupConfigurations.cpp b/examples/configurations/SetupConfigurations.cpp index d91bbea0..284500d6 100644 --- a/examples/configurations/SetupConfigurations.cpp +++ b/examples/configurations/SetupConfigurations.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/data-generators/CMakeLists.txt b/examples/data-generators/CMakeLists.txt index eafd867d..99324d4d 100644 --- a/examples/data-generators/CMakeLists.txt +++ b/examples/data-generators/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/data-generators/SyntheticDataGeneration.cpp b/examples/data-generators/SyntheticDataGeneration.cpp index afa3c6a5..e448480f 100644 --- a/examples/data-generators/SyntheticDataGeneration.cpp +++ b/examples/data-generators/SyntheticDataGeneration.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/data-loader/CMakeLists.txt b/examples/data-loader/CMakeLists.txt index 007661d8..4e1c4ae5 100644 --- a/examples/data-loader/CMakeLists.txt +++ b/examples/data-loader/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/data-loader/CSVLoader.cpp b/examples/data-loader/CSVLoader.cpp index fed01c80..701f3b10 100644 --- a/examples/data-loader/CSVLoader.cpp +++ b/examples/data-loader/CSVLoader.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/descriptors/CMakeLists.txt b/examples/descriptors/CMakeLists.txt index f0d9a333..eb3cf6c2 100644 --- a/examples/descriptors/CMakeLists.txt +++ b/examples/descriptors/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/descriptors/ChameleonDescriptor.cpp b/examples/descriptors/ChameleonDescriptor.cpp index 3f5e9c28..929403c5 100644 --- a/examples/descriptors/ChameleonDescriptor.cpp +++ b/examples/descriptors/ChameleonDescriptor.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/descriptors/ChameleonToHicmaConverter.cpp b/examples/descriptors/ChameleonToHicmaConverter.cpp index 0b1ceed3..1f60457c 100644 --- a/examples/descriptors/ChameleonToHicmaConverter.cpp +++ b/examples/descriptors/ChameleonToHicmaConverter.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/descriptors/HicmaDescriptor.cpp b/examples/descriptors/HicmaDescriptor.cpp index ce38a255..5c81ae16 100644 --- a/examples/descriptors/HicmaDescriptor.cpp +++ b/examples/descriptors/HicmaDescriptor.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/end-to-end/CMakeLists.txt b/examples/end-to-end/CMakeLists.txt index a4b0f472..4131b826 100644 --- a/examples/end-to-end/CMakeLists.txt +++ b/examples/end-to-end/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/end-to-end/DataGeneration.cpp b/examples/end-to-end/DataGeneration.cpp index f392be6e..fde12d35 100644 --- a/examples/end-to-end/DataGeneration.cpp +++ b/examples/end-to-end/DataGeneration.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/end-to-end/DataGenerationAndModeling.cpp b/examples/end-to-end/DataGenerationAndModeling.cpp index 092f9bcb..30fdf5d8 100644 --- a/examples/end-to-end/DataGenerationAndModeling.cpp +++ b/examples/end-to-end/DataGenerationAndModeling.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/end-to-end/DataGenerationAndPrediction.cpp b/examples/end-to-end/DataGenerationAndPrediction.cpp index 4845dcfc..7150c78e 100644 --- a/examples/end-to-end/DataGenerationAndPrediction.cpp +++ b/examples/end-to-end/DataGenerationAndPrediction.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp index bb770e2e..746efdd8 100644 --- a/examples/end-to-end/DataGenerationModelingAndPrediction.cpp +++ b/examples/end-to-end/DataGenerationModelingAndPrediction.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/end-to-end/DataModeling.cpp b/examples/end-to-end/DataModeling.cpp index 14a559f9..3ce1070f 100644 --- a/examples/end-to-end/DataModeling.cpp +++ b/examples/end-to-end/DataModeling.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/end-to-end/DataPrediction.cpp b/examples/end-to-end/DataPrediction.cpp index e7ea846a..dbe1a1f8 100644 --- a/examples/end-to-end/DataPrediction.cpp +++ b/examples/end-to-end/DataPrediction.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/hardware/CMakeLists.txt b/examples/hardware/CMakeLists.txt index 6acda72d..82d5e8f9 100644 --- a/examples/hardware/CMakeLists.txt +++ b/examples/hardware/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/examples/hardware/ExaGeoStatHardware.cpp b/examples/hardware/ExaGeoStatHardware.cpp index 3372d435..0710943f 100644 --- a/examples/hardware/ExaGeoStatHardware.cpp +++ b/examples/hardware/ExaGeoStatHardware.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp index bfa42154..f48af464 100644 --- a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp +++ b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/api/ExaGeoStat.hpp b/inst/include/api/ExaGeoStat.hpp index 4bbc0590..40978215 100644 --- a/inst/include/api/ExaGeoStat.hpp +++ b/inst/include/api/ExaGeoStat.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/common/Definitions.hpp b/inst/include/common/Definitions.hpp index 78fdb067..3ca35b9c 100644 --- a/inst/include/common/Definitions.hpp +++ b/inst/include/common/Definitions.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/common/PluginRegistry.hpp b/inst/include/common/PluginRegistry.hpp index bcde1187..b59a9d7f 100644 --- a/inst/include/common/PluginRegistry.hpp +++ b/inst/include/common/PluginRegistry.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index 1d9b221d..954d480a 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-generators/DataGenerator.hpp b/inst/include/data-generators/DataGenerator.hpp index afe875e7..2e5d4344 100644 --- a/inst/include/data-generators/DataGenerator.hpp +++ b/inst/include/data-generators/DataGenerator.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-generators/LocationGenerator.hpp b/inst/include/data-generators/LocationGenerator.hpp index d2b3c7e9..798d2271 100644 --- a/inst/include/data-generators/LocationGenerator.hpp +++ b/inst/include/data-generators/LocationGenerator.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-generators/concrete/SyntheticGenerator.hpp b/inst/include/data-generators/concrete/SyntheticGenerator.hpp index b61c366d..d159f161 100644 --- a/inst/include/data-generators/concrete/SyntheticGenerator.hpp +++ b/inst/include/data-generators/concrete/SyntheticGenerator.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-loader/DataLoader.hpp b/inst/include/data-loader/DataLoader.hpp index 80ffe0f8..5f530bd6 100644 --- a/inst/include/data-loader/DataLoader.hpp +++ b/inst/include/data-loader/DataLoader.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-loader/concrete/CSVLoader.hpp b/inst/include/data-loader/concrete/CSVLoader.hpp index ce3e2de9..52b185bd 100644 --- a/inst/include/data-loader/concrete/CSVLoader.hpp +++ b/inst/include/data-loader/concrete/CSVLoader.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-units/DescriptorData.hpp b/inst/include/data-units/DescriptorData.hpp index fe9061f4..5d2a9192 100644 --- a/inst/include/data-units/DescriptorData.hpp +++ b/inst/include/data-units/DescriptorData.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-units/ExaGeoStatData.hpp b/inst/include/data-units/ExaGeoStatData.hpp index 29389eb2..bc9017bf 100644 --- a/inst/include/data-units/ExaGeoStatData.hpp +++ b/inst/include/data-units/ExaGeoStatData.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-units/Locations.hpp b/inst/include/data-units/Locations.hpp index 3d9d7f61..72115ddd 100644 --- a/inst/include/data-units/Locations.hpp +++ b/inst/include/data-units/Locations.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp index eb075807..fbf1eb3d 100644 --- a/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp +++ b/inst/include/data-units/descriptor/ExaGeoStatDescriptor.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp index b5ebf4ea..cabe7557 100644 --- a/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/ChameleonDescriptor.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp index 0575a87d..0ffb27fe 100644 --- a/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp +++ b/inst/include/data-units/descriptor/concrete/HicmaDescriptor.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index 5e08faa7..40622648 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/helpers/BasselFunction.hpp b/inst/include/helpers/BasselFunction.hpp index c7d2a7b2..2ce49385 100644 --- a/inst/include/helpers/BasselFunction.hpp +++ b/inst/include/helpers/BasselFunction.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/helpers/ByteHandler.hpp b/inst/include/helpers/ByteHandler.hpp index f8139aa6..2a7f41d1 100644 --- a/inst/include/helpers/ByteHandler.hpp +++ b/inst/include/helpers/ByteHandler.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/helpers/CommunicatorMPI.hpp b/inst/include/helpers/CommunicatorMPI.hpp index b3aa9245..1b1f1bb9 100644 --- a/inst/include/helpers/CommunicatorMPI.hpp +++ b/inst/include/helpers/CommunicatorMPI.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/helpers/DistanceCalculationHelpers.hpp b/inst/include/helpers/DistanceCalculationHelpers.hpp index 61e443cd..bf74c31a 100644 --- a/inst/include/helpers/DistanceCalculationHelpers.hpp +++ b/inst/include/helpers/DistanceCalculationHelpers.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/Kernel.hpp b/inst/include/kernels/Kernel.hpp index 65e23aab..3d018d5c 100644 --- a/inst/include/kernels/Kernel.hpp +++ b/inst/include/kernels/Kernel.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/BivariateMaternFlexible.hpp b/inst/include/kernels/concrete/BivariateMaternFlexible.hpp index ebdf3ed8..a9abaa36 100644 --- a/inst/include/kernels/concrete/BivariateMaternFlexible.hpp +++ b/inst/include/kernels/concrete/BivariateMaternFlexible.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp b/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp index 2ff6b0d5..c112fa55 100644 --- a/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp +++ b/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp b/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp index a32670e7..e7e19ca6 100644 --- a/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp +++ b/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp b/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp index 53c4cad9..b2087646 100644 --- a/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp +++ b/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp b/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp index 905433ed..efd4d6f0 100644 --- a/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp +++ b/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp index 21836598..0f3ecaf7 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp index 001dacfd..ddc1681b 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp index 081d8932..41fe5cc0 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp index 722f3d59..c5293e95 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp index c6513f30..bc6032e2 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp index 125cd7ca..047f9029 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp index 9e23ca2f..53fe8f21 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternDnu.hpp b/inst/include/kernels/concrete/UnivariateMaternDnu.hpp index c8779765..2476789c 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDnu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDnu.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp b/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp index 0e9a74c8..bf39a24a 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp b/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp index d582faf3..321913e0 100644 --- a/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp index d0e377bc..b0e5ca91 100644 --- a/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateMaternStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternStationary.hpp index e0cc2cdc..8fb3757c 100644 --- a/inst/include/kernels/concrete/UnivariateMaternStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternStationary.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp b/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp index 68b3f57f..09939e57 100644 --- a/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp +++ b/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp b/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp index 946fe095..27233ba1 100644 --- a/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp index cc42108f..fc789af8 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraFactory.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp index e8135665..251d6319 100644 --- a/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp +++ b/inst/include/linear-algebra-solvers/LinearAlgebraMethods.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp index a13f4082..8653bfc5 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp index ac84ce12..81a3c05e 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp b/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp index be3ed402..f8e96954 100644 --- a/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp +++ b/inst/include/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp index 29bef95b..4c488a3d 100644 --- a/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp +++ b/inst/include/linear-algebra-solvers/concrete/hicma/tlr/HicmaImplementation.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/prediction/Prediction.hpp b/inst/include/prediction/Prediction.hpp index f69d1265..96efb614 100644 --- a/inst/include/prediction/Prediction.hpp +++ b/inst/include/prediction/Prediction.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp index 40e65cbb..c8dc3b70 100644 --- a/inst/include/prediction/PredictionAuxiliaryFunctions.hpp +++ b/inst/include/prediction/PredictionAuxiliaryFunctions.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/prediction/PredictionHelpers.hpp b/inst/include/prediction/PredictionHelpers.hpp index 3bd1b954..b4be1875 100644 --- a/inst/include/prediction/PredictionHelpers.hpp +++ b/inst/include/prediction/PredictionHelpers.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/results/Results.hpp b/inst/include/results/Results.hpp index 4e21786b..1f21c6b9 100644 --- a/inst/include/results/Results.hpp +++ b/inst/include/results/Results.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/RuntimeFunctions.hpp b/inst/include/runtime/RuntimeFunctions.hpp index 831cf769..83cbdbe8 100644 --- a/inst/include/runtime/RuntimeFunctions.hpp +++ b/inst/include/runtime/RuntimeFunctions.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/StarPuCodeletsHeaders.hpp b/inst/include/runtime/starpu/StarPuCodeletsHeaders.hpp index 542dca14..caacc019 100644 --- a/inst/include/runtime/starpu/StarPuCodeletsHeaders.hpp +++ b/inst/include/runtime/starpu/StarPuCodeletsHeaders.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/dcmg-codelet.hpp b/inst/include/runtime/starpu/concrete/dcmg-codelet.hpp index 37df06f9..6c45b139 100644 --- a/inst/include/runtime/starpu/concrete/dcmg-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/dcmg-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/ddotp-codelet.hpp b/inst/include/runtime/starpu/concrete/ddotp-codelet.hpp index 4273d437..45061fd8 100644 --- a/inst/include/runtime/starpu/concrete/ddotp-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/ddotp-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp b/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp index 52bd2dff..8fa47e0d 100644 --- a/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/dmdet-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/dmloe-mmom-codelet.hpp b/inst/include/runtime/starpu/concrete/dmloe-mmom-codelet.hpp index f9efee1b..b995843c 100644 --- a/inst/include/runtime/starpu/concrete/dmloe-mmom-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/dmloe-mmom-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/dmse-bivariate-codelet.hpp b/inst/include/runtime/starpu/concrete/dmse-bivariate-codelet.hpp index 5bca487f..8a0043eb 100644 --- a/inst/include/runtime/starpu/concrete/dmse-bivariate-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/dmse-bivariate-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/dmse-codelet.hpp b/inst/include/runtime/starpu/concrete/dmse-codelet.hpp index 59b78d56..4e30408c 100644 --- a/inst/include/runtime/starpu/concrete/dmse-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/dmse-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp b/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp index af04553b..d10ce526 100644 --- a/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/dtrace-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/dzcpy-codelet.hpp b/inst/include/runtime/starpu/concrete/dzcpy-codelet.hpp index 45459a61..c8980eee 100644 --- a/inst/include/runtime/starpu/concrete/dzcpy-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/dzcpy-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/gaussian-to-non-codelet.hpp b/inst/include/runtime/starpu/concrete/gaussian-to-non-codelet.hpp index ad3ac512..932e1dd2 100644 --- a/inst/include/runtime/starpu/concrete/gaussian-to-non-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/gaussian-to-non-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/non-gaussian-loglike-codelet.hpp b/inst/include/runtime/starpu/concrete/non-gaussian-loglike-codelet.hpp index ea5cfe34..cc103f24 100644 --- a/inst/include/runtime/starpu/concrete/non-gaussian-loglike-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/non-gaussian-loglike-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp b/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp index c2605f6a..c79fd79a 100644 --- a/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/non-gaussian-transform-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/stride-vec-codelet.hpp b/inst/include/runtime/starpu/concrete/stride-vec-codelet.hpp index 90d76a59..687c4a17 100644 --- a/inst/include/runtime/starpu/concrete/stride-vec-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/stride-vec-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/concrete/tri-stride-vec-codelet.hpp b/inst/include/runtime/starpu/concrete/tri-stride-vec-codelet.hpp index 6fc939e6..ae26f348 100644 --- a/inst/include/runtime/starpu/concrete/tri-stride-vec-codelet.hpp +++ b/inst/include/runtime/starpu/concrete/tri-stride-vec-codelet.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/helpers/StarPuHelpers.hpp b/inst/include/runtime/starpu/helpers/StarPuHelpers.hpp index fa2ed425..cfaa98f5 100644 --- a/inst/include/runtime/starpu/helpers/StarPuHelpers.hpp +++ b/inst/include/runtime/starpu/helpers/StarPuHelpers.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/helpers/StarPuHelpersFactory.hpp b/inst/include/runtime/starpu/helpers/StarPuHelpersFactory.hpp index 656e02a4..e8d9656c 100644 --- a/inst/include/runtime/starpu/helpers/StarPuHelpersFactory.hpp +++ b/inst/include/runtime/starpu/helpers/StarPuHelpersFactory.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.hpp b/inst/include/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.hpp index bbc1ae21..ee597973 100644 --- a/inst/include/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.hpp +++ b/inst/include/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp b/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp index fefa6102..c59d2ff3 100644 --- a/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp +++ b/inst/include/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/utilities/EnumStringParser.hpp b/inst/include/utilities/EnumStringParser.hpp index 6d3ed072..ee7201bb 100644 --- a/inst/include/utilities/EnumStringParser.hpp +++ b/inst/include/utilities/EnumStringParser.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/utilities/ErrorHandler.hpp b/inst/include/utilities/ErrorHandler.hpp index 21e51b15..41b24628 100644 --- a/inst/include/utilities/ErrorHandler.hpp +++ b/inst/include/utilities/ErrorHandler.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/inst/include/utilities/Logger.hpp b/inst/include/utilities/Logger.hpp index c0e2b5db..04fbb1c6 100644 --- a/inst/include/utilities/Logger.hpp +++ b/inst/include/utilities/Logger.hpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/man/Data.Rd b/man/Data.Rd index 3e55914a..95be1ec6 100644 --- a/man/Data.Rd +++ b/man/Data.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file Data.Rd -% @brief roxygen2 documentation for the R wrapper of ExaGeoStatData class +% @brief roxygen2 documentation for the R Interface of ExaGeoStatData class % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/Hardware.Rd b/man/Hardware.Rd index 142cb2ab..2684d6e2 100644 --- a/man/Hardware.Rd +++ b/man/Hardware.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file Hardware.Rd -% @brief roxygen2 documentation for the R wrapper of ExaGeoStatHardware class. +% @brief roxygen2 documentation for the R Interface of ExaGeoStatHardware class. % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/fisher.Rd b/man/fisher.Rd index 9d038c3a..d94b582f 100644 --- a/man/fisher.Rd +++ b/man/fisher.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file fisher.Rd -% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatFisher function. +% @brief roxygen2 documentation for the R Interface of R_ExaGeoStatFisher function. % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/get_Z_measurement_vector.Rd b/man/get_Z_measurement_vector.Rd index 1054de28..c19c42ed 100644 --- a/man/get_Z_measurement_vector.Rd +++ b/man/get_Z_measurement_vector.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file get_Z_measurement_vector.Rd -% @brief roxygen2 documentation for the R wrapper of get_Z_measurment_vector function. +% @brief roxygen2 documentation for the R Interface of get_Z_measurment_vector function. % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/get_locationsX.Rd b/man/get_locationsX.Rd index fe652279..caa777e8 100644 --- a/man/get_locationsX.Rd +++ b/man/get_locationsX.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file get_locationsX.Rd -% @brief roxygen2 documentation for the R wrapper of get_locationsX function for X coordinates. +% @brief roxygen2 documentation for the R Interface of get_locationsX function for X coordinates. % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/get_locationsY.Rd b/man/get_locationsY.Rd index c3da0bd5..a6367b25 100644 --- a/man/get_locationsY.Rd +++ b/man/get_locationsY.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file get_locationsY.Rd -% @brief roxygen2 documentation for the R wrapper of get_locationsY function for Y coordinates. +% @brief roxygen2 documentation for the R Interface of get_locationsY function for Y coordinates. % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/get_locationsZ.Rd b/man/get_locationsZ.Rd index daaa407c..412eab04 100644 --- a/man/get_locationsZ.Rd +++ b/man/get_locationsZ.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file get_locationsZ.Rd -% @brief roxygen2 documentation for the R wrapper of get_locationsZ function for Z coordinates. +% @brief roxygen2 documentation for the R Interface of get_locationsZ function for Z coordinates. % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/idw.Rd b/man/idw.Rd index bc5bbf54..82b93e07 100644 --- a/man/idw.Rd +++ b/man/idw.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file idw.Rd -% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatIDW function. +% @brief roxygen2 documentation for the R Interface of R_ExaGeoStatIDW function. % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/mloe_mmom.Rd b/man/mloe_mmom.Rd index e8c7c382..5cf8e739 100644 --- a/man/mloe_mmom.Rd +++ b/man/mloe_mmom.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file mloe_mmom.Rd -% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatMLOE_MMOM function +% @brief roxygen2 documentation for the R Interface of R_ExaGeoStatMLOE_MMOM function % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/model_data.Rd b/man/model_data.Rd index 3d5bfbe7..645656d4 100644 --- a/man/model_data.Rd +++ b/man/model_data.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file model_data.Rd -% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatModelData function. +% @brief roxygen2 documentation for the R Interface of R_ExaGeoStatModelData function. % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/predict_data.Rd b/man/predict_data.Rd index 3bf54c4c..50cb490a 100644 --- a/man/predict_data.Rd +++ b/man/predict_data.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file predict_data.Rd -% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatPredictData function. +% @brief roxygen2 documentation for the R Interface of R_ExaGeoStatPredictData function. % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/man/simulate_data.Rd b/man/simulate_data.Rd index 25bf8553..229d76e4 100644 --- a/man/simulate_data.Rd +++ b/man/simulate_data.Rd @@ -1,10 +1,10 @@ -% Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +% Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). % @file simulate_data.Rd -% @brief roxygen2 documentation for the R wrapper of R_ExaGeoStatLoadData function +% @brief roxygen2 documentation for the R Interface of R_ExaGeoStatLoadData function % @version 1.1.0 % @author Mahmoud ElKarargy % @date 2024-03-17 diff --git a/scripts/Benchmarking.sh b/scripts/Benchmarking.sh index e14bafd8..ac72b5f6 100644 --- a/scripts/Benchmarking.sh +++ b/scripts/Benchmarking.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a8470ac..beb9378f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/Makefile b/src/Makefile index 46aced6a..af240108 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/Rcpp-adapters/CMakeLists.txt b/src/Rcpp-adapters/CMakeLists.txt index 50006846..d161a223 100644 --- a/src/Rcpp-adapters/CMakeLists.txt +++ b/src/Rcpp-adapters/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/Rcpp-adapters/FunctionsAdapter.cpp b/src/Rcpp-adapters/FunctionsAdapter.cpp index 3cebb7c3..2c52ad59 100644 --- a/src/Rcpp-adapters/FunctionsAdapter.cpp +++ b/src/Rcpp-adapters/FunctionsAdapter.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/Rcpp-adapters/RcppExports.cpp b/src/Rcpp-adapters/RcppExports.cpp index a8ea7f88..075e5c1f 100644 --- a/src/Rcpp-adapters/RcppExports.cpp +++ b/src/Rcpp-adapters/RcppExports.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/Rcpp-adapters/RcppModules.cpp b/src/Rcpp-adapters/RcppModules.cpp index c00edcb9..284a205f 100644 --- a/src/Rcpp-adapters/RcppModules.cpp +++ b/src/Rcpp-adapters/RcppModules.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index 1c2960e6..223513e0 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/api/ExaGeoStat.cpp b/src/api/ExaGeoStat.cpp index 0af851c4..5ffc06d5 100644 --- a/src/api/ExaGeoStat.cpp +++ b/src/api/ExaGeoStat.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/configurations/CMakeLists.txt b/src/configurations/CMakeLists.txt index 5193edb9..3575e1a3 100644 --- a/src/configurations/CMakeLists.txt +++ b/src/configurations/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/configurations/Configurations.cpp b/src/configurations/Configurations.cpp index 6d0c1586..4edb4848 100644 --- a/src/configurations/Configurations.cpp +++ b/src/configurations/Configurations.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-generators/CMakeLists.txt b/src/data-generators/CMakeLists.txt index b5669976..e5e32bdb 100644 --- a/src/data-generators/CMakeLists.txt +++ b/src/data-generators/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-generators/DataGenerator.cpp b/src/data-generators/DataGenerator.cpp index 761c5751..dd4bec8b 100644 --- a/src/data-generators/DataGenerator.cpp +++ b/src/data-generators/DataGenerator.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-generators/LocationGenerator.cpp b/src/data-generators/LocationGenerator.cpp index fcdff5f1..5703e04b 100644 --- a/src/data-generators/LocationGenerator.cpp +++ b/src/data-generators/LocationGenerator.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-generators/concrete/CMakeLists.txt b/src/data-generators/concrete/CMakeLists.txt index d80f538e..bca0dfdb 100644 --- a/src/data-generators/concrete/CMakeLists.txt +++ b/src/data-generators/concrete/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-generators/concrete/SyntheticGenerator.cpp b/src/data-generators/concrete/SyntheticGenerator.cpp index 70c750a7..85cfa7ec 100644 --- a/src/data-generators/concrete/SyntheticGenerator.cpp +++ b/src/data-generators/concrete/SyntheticGenerator.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-loader/CMakeLists.txt b/src/data-loader/CMakeLists.txt index 9338b55c..03a54e86 100644 --- a/src/data-loader/CMakeLists.txt +++ b/src/data-loader/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-loader/DataLoader.cpp b/src/data-loader/DataLoader.cpp index 759980bc..fb7ca2fd 100644 --- a/src/data-loader/DataLoader.cpp +++ b/src/data-loader/DataLoader.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-loader/concrete/CMakeLists.txt b/src/data-loader/concrete/CMakeLists.txt index 72b458bd..88180c80 100644 --- a/src/data-loader/concrete/CMakeLists.txt +++ b/src/data-loader/concrete/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-loader/concrete/CSVLoader.cpp b/src/data-loader/concrete/CSVLoader.cpp index e25bb057..bfd153cd 100644 --- a/src/data-loader/concrete/CSVLoader.cpp +++ b/src/data-loader/concrete/CSVLoader.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-units/CMakeLists.txt b/src/data-units/CMakeLists.txt index ae2602f8..85e48392 100644 --- a/src/data-units/CMakeLists.txt +++ b/src/data-units/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-units/DescriptorData.cpp b/src/data-units/DescriptorData.cpp index 709c85ad..019503e7 100644 --- a/src/data-units/DescriptorData.cpp +++ b/src/data-units/DescriptorData.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-units/ExaGeoStatData.cpp b/src/data-units/ExaGeoStatData.cpp index 05185123..a1c912a5 100644 --- a/src/data-units/ExaGeoStatData.cpp +++ b/src/data-units/ExaGeoStatData.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-units/Locations.cpp b/src/data-units/Locations.cpp index 65c1691c..abe0572c 100644 --- a/src/data-units/Locations.cpp +++ b/src/data-units/Locations.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-units/descriptor/CMakeLists.txt b/src/data-units/descriptor/CMakeLists.txt index 316da3ef..352aec33 100644 --- a/src/data-units/descriptor/CMakeLists.txt +++ b/src/data-units/descriptor/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp index 7dd895ff..5d6cd730 100644 --- a/src/data-units/descriptor/ExaGeoStatDescriptor.cpp +++ b/src/data-units/descriptor/ExaGeoStatDescriptor.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-units/descriptor/concrete/CMakeLists.txt b/src/data-units/descriptor/concrete/CMakeLists.txt index ca3190e8..6bb197d4 100644 --- a/src/data-units/descriptor/concrete/CMakeLists.txt +++ b/src/data-units/descriptor/concrete/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp b/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp index 706e66d9..964215ec 100644 --- a/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp +++ b/src/data-units/descriptor/concrete/ChameleonDescriptor.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp index 55f12abe..9a4e6724 100644 --- a/src/data-units/descriptor/concrete/HicmaDescriptor.cpp +++ b/src/data-units/descriptor/concrete/HicmaDescriptor.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/hardware/CMakeLists.txt b/src/hardware/CMakeLists.txt index 40242b3b..98fc7760 100644 --- a/src/hardware/CMakeLists.txt +++ b/src/hardware/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index cd970c03..0b7de213 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/helpers/BasselFunction.cpp b/src/helpers/BasselFunction.cpp index 55634bd4..b0b58689 100644 --- a/src/helpers/BasselFunction.cpp +++ b/src/helpers/BasselFunction.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/helpers/ByteHandler.cpp b/src/helpers/ByteHandler.cpp index 6f848487..252858b0 100644 --- a/src/helpers/ByteHandler.cpp +++ b/src/helpers/ByteHandler.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/helpers/CMakeLists.txt b/src/helpers/CMakeLists.txt index 06a01857..f2f6ef82 100644 --- a/src/helpers/CMakeLists.txt +++ b/src/helpers/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/helpers/CommunicatorMPI.cpp b/src/helpers/CommunicatorMPI.cpp index 26b156dc..0abc6d1c 100644 --- a/src/helpers/CommunicatorMPI.cpp +++ b/src/helpers/CommunicatorMPI.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/helpers/DistanceCalculationHelpers.cpp b/src/helpers/DistanceCalculationHelpers.cpp index 33aac868..65487536 100644 --- a/src/helpers/DistanceCalculationHelpers.cpp +++ b/src/helpers/DistanceCalculationHelpers.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/CMakeLists.txt b/src/kernels/CMakeLists.txt index e76e4862..1c6b67cd 100644 --- a/src/kernels/CMakeLists.txt +++ b/src/kernels/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/Kernel.cpp b/src/kernels/Kernel.cpp index 78c992ee..931a9d97 100644 --- a/src/kernels/Kernel.cpp +++ b/src/kernels/Kernel.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/BivariateMaternFlexible.cpp b/src/kernels/concrete/BivariateMaternFlexible.cpp index 9fc50947..1ae4e414 100644 --- a/src/kernels/concrete/BivariateMaternFlexible.cpp +++ b/src/kernels/concrete/BivariateMaternFlexible.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/BivariateMaternParsimonious.cpp b/src/kernels/concrete/BivariateMaternParsimonious.cpp index 1431aec7..1a200997 100644 --- a/src/kernels/concrete/BivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/BivariateMaternParsimonious.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp index a3a49765..06693b34 100644 --- a/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/BivariateSpacetimeMaternStationary.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/TrivariateMaternParsimonious.cpp b/src/kernels/concrete/TrivariateMaternParsimonious.cpp index 1734861f..628a7b6f 100644 --- a/src/kernels/concrete/TrivariateMaternParsimonious.cpp +++ b/src/kernels/concrete/TrivariateMaternParsimonious.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateExpNonGaussian.cpp b/src/kernels/concrete/UnivariateExpNonGaussian.cpp index d207cbfc..1507d6ca 100644 --- a/src/kernels/concrete/UnivariateExpNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateExpNonGaussian.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternDbeta.cpp b/src/kernels/concrete/UnivariateMaternDbeta.cpp index fd42dec9..6ebaf025 100644 --- a/src/kernels/concrete/UnivariateMaternDbeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDbeta.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp index c1370efa..36ae025b 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaBeta.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp index e85f3138..955ae083 100644 --- a/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdbetaNu.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp index ba1f0833..2869e0fb 100644 --- a/src/kernels/concrete/UnivariateMaternDdnuNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdnuNu.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp index 6d4af4b4..600c8c5f 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquare.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp index f6eacdc6..2111d1d0 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp index 1c934301..20f2e7de 100644 --- a/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp +++ b/src/kernels/concrete/UnivariateMaternDdsigmaSquareNu.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternDnu.cpp b/src/kernels/concrete/UnivariateMaternDnu.cpp index 4d8ee30a..9fe8dd67 100644 --- a/src/kernels/concrete/UnivariateMaternDnu.cpp +++ b/src/kernels/concrete/UnivariateMaternDnu.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp index 406f00be..d4e7ec77 100644 --- a/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp +++ b/src/kernels/concrete/UnivariateMaternDsigmaSquare.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp index cdb458fe..070a1e70 100644 --- a/src/kernels/concrete/UnivariateMaternNonGaussian.cpp +++ b/src/kernels/concrete/UnivariateMaternNonGaussian.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp index 19a43a6f..ab960043 100644 --- a/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternNuggetsStationary.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateMaternStationary.cpp b/src/kernels/concrete/UnivariateMaternStationary.cpp index 4facc1e3..790d9888 100644 --- a/src/kernels/concrete/UnivariateMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateMaternStationary.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariatePowExpStationary.cpp b/src/kernels/concrete/UnivariatePowExpStationary.cpp index 1563025a..e4fa247f 100644 --- a/src/kernels/concrete/UnivariatePowExpStationary.cpp +++ b/src/kernels/concrete/UnivariatePowExpStationary.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp index b7a7eb32..38ab7a9e 100644 --- a/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp +++ b/src/kernels/concrete/UnivariateSpacetimeMaternStationary.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/linear-algebra-solvers/CMakeLists.txt b/src/linear-algebra-solvers/CMakeLists.txt index 0eb0f8a9..8a9eba80 100644 --- a/src/linear-algebra-solvers/CMakeLists.txt +++ b/src/linear-algebra-solvers/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp index 040d2e11..0d72dbad 100644 --- a/src/linear-algebra-solvers/LinearAlgebraFactory.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraFactory.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index 9b73f1a3..8573008c 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/linear-algebra-solvers/concrete/CMakeLists.txt b/src/linear-algebra-solvers/concrete/CMakeLists.txt index af1c7e3e..74df59f8 100644 --- a/src/linear-algebra-solvers/concrete/CMakeLists.txt +++ b/src/linear-algebra-solvers/concrete/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp index 47857c7d..8ec6abe8 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/ChameleonImplementation.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp b/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp index 47eca4e8..f08c8c3b 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/dense/ChameleonDense.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp b/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp index 2953d7c6..cc58316b 100644 --- a/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp +++ b/src/linear-algebra-solvers/concrete/chameleon/dst/ChameleonDST.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp index ad082144..d2b0252e 100644 --- a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/prediction/CMakeLists.txt b/src/prediction/CMakeLists.txt index d43bde5c..1de78426 100644 --- a/src/prediction/CMakeLists.txt +++ b/src/prediction/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/prediction/Prediction.cpp b/src/prediction/Prediction.cpp index b3967ac6..2cf565c2 100644 --- a/src/prediction/Prediction.cpp +++ b/src/prediction/Prediction.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/prediction/PredictionAuxiliaryFunctions.cpp b/src/prediction/PredictionAuxiliaryFunctions.cpp index e02466a9..1dc84d28 100644 --- a/src/prediction/PredictionAuxiliaryFunctions.cpp +++ b/src/prediction/PredictionAuxiliaryFunctions.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/prediction/PredictionHelpers.cpp b/src/prediction/PredictionHelpers.cpp index 2e547678..e4d1b8e9 100644 --- a/src/prediction/PredictionHelpers.cpp +++ b/src/prediction/PredictionHelpers.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/results/CMakeLists.txt b/src/results/CMakeLists.txt index 72002da3..e77a4d86 100644 --- a/src/results/CMakeLists.txt +++ b/src/results/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/results/Results.cpp b/src/results/Results.cpp index 69cfc6ad..3637fe66 100644 --- a/src/results/Results.cpp +++ b/src/results/Results.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt index e1ea8712..74d34df3 100644 --- a/src/runtime/CMakeLists.txt +++ b/src/runtime/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/parsec/CMakeLists.txt b/src/runtime/parsec/CMakeLists.txt index 24ed6d70..f1b427db 100644 --- a/src/runtime/parsec/CMakeLists.txt +++ b/src/runtime/parsec/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/parsec/ParsecFunctions.cpp b/src/runtime/parsec/ParsecFunctions.cpp index 0e5a2e29..65b0073d 100644 --- a/src/runtime/parsec/ParsecFunctions.cpp +++ b/src/runtime/parsec/ParsecFunctions.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/CMakeLists.txt b/src/runtime/starpu/CMakeLists.txt index f2af4f73..1fce6410 100644 --- a/src/runtime/starpu/CMakeLists.txt +++ b/src/runtime/starpu/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). @@ -27,7 +27,7 @@ set(SOURCES # File documentation set(DOCUMENTATION_STRING " -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/StarPuFunctions.cpp b/src/runtime/starpu/StarPuFunctions.cpp index 9ecbf050..bf15d66a 100644 --- a/src/runtime/starpu/StarPuFunctions.cpp +++ b/src/runtime/starpu/StarPuFunctions.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/dcmg-codelet.cpp b/src/runtime/starpu/concrete/dcmg-codelet.cpp index a70053b1..71827f84 100644 --- a/src/runtime/starpu/concrete/dcmg-codelet.cpp +++ b/src/runtime/starpu/concrete/dcmg-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/ddotp-codelet.cpp b/src/runtime/starpu/concrete/ddotp-codelet.cpp index d14c50cd..f9604a8b 100644 --- a/src/runtime/starpu/concrete/ddotp-codelet.cpp +++ b/src/runtime/starpu/concrete/ddotp-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/dmdet-codelet.cpp b/src/runtime/starpu/concrete/dmdet-codelet.cpp index d6a65bbc..503ebc1d 100644 --- a/src/runtime/starpu/concrete/dmdet-codelet.cpp +++ b/src/runtime/starpu/concrete/dmdet-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp b/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp index 77e02c29..104661f2 100644 --- a/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp +++ b/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/dmse-bivariate-codelet.cpp b/src/runtime/starpu/concrete/dmse-bivariate-codelet.cpp index 978dc1bb..badbfa71 100644 --- a/src/runtime/starpu/concrete/dmse-bivariate-codelet.cpp +++ b/src/runtime/starpu/concrete/dmse-bivariate-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/dmse-codelet.cpp b/src/runtime/starpu/concrete/dmse-codelet.cpp index 2bcace90..657dbdc9 100644 --- a/src/runtime/starpu/concrete/dmse-codelet.cpp +++ b/src/runtime/starpu/concrete/dmse-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/dtrace-codelet.cpp b/src/runtime/starpu/concrete/dtrace-codelet.cpp index a5ecdda7..1572becb 100644 --- a/src/runtime/starpu/concrete/dtrace-codelet.cpp +++ b/src/runtime/starpu/concrete/dtrace-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/dzcpy-codelet.cpp b/src/runtime/starpu/concrete/dzcpy-codelet.cpp index c279d4a5..6c213f2e 100644 --- a/src/runtime/starpu/concrete/dzcpy-codelet.cpp +++ b/src/runtime/starpu/concrete/dzcpy-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp b/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp index ccfb2f78..81db6fd3 100644 --- a/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp +++ b/src/runtime/starpu/concrete/gaussian-to-non-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp b/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp index d0dbdac6..d425ca7e 100644 --- a/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp +++ b/src/runtime/starpu/concrete/non-gaussian-loglike-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/non-gaussian-transform-codelet.cpp b/src/runtime/starpu/concrete/non-gaussian-transform-codelet.cpp index a6c5900a..0cce4daf 100644 --- a/src/runtime/starpu/concrete/non-gaussian-transform-codelet.cpp +++ b/src/runtime/starpu/concrete/non-gaussian-transform-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/stride-vec-codelet.cpp b/src/runtime/starpu/concrete/stride-vec-codelet.cpp index 52c23a10..5c8774e9 100644 --- a/src/runtime/starpu/concrete/stride-vec-codelet.cpp +++ b/src/runtime/starpu/concrete/stride-vec-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/concrete/tri-stride-vec-codelet.cpp b/src/runtime/starpu/concrete/tri-stride-vec-codelet.cpp index 6006e553..44dc080d 100644 --- a/src/runtime/starpu/concrete/tri-stride-vec-codelet.cpp +++ b/src/runtime/starpu/concrete/tri-stride-vec-codelet.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/helpers/CMakeLists.txt b/src/runtime/starpu/helpers/CMakeLists.txt index 63510dd9..295fba30 100644 --- a/src/runtime/starpu/helpers/CMakeLists.txt +++ b/src/runtime/starpu/helpers/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/helpers/StarPuHelpersFactory.cpp b/src/runtime/starpu/helpers/StarPuHelpersFactory.cpp index d703e496..fdb866fa 100644 --- a/src/runtime/starpu/helpers/StarPuHelpersFactory.cpp +++ b/src/runtime/starpu/helpers/StarPuHelpersFactory.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.cpp b/src/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.cpp index 458bb4e1..7d122d8c 100644 --- a/src/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.cpp +++ b/src/runtime/starpu/helpers/concrete/ChameleonStarPuHelpers.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/src/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.cpp b/src/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.cpp index fa43b9e7..5b82c4fb 100644 --- a/src/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.cpp +++ b/src/runtime/starpu/helpers/concrete/HicmaStarPuHelpers.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/R-tests/TestDataGeneration.R b/tests/R-tests/TestDataGeneration.R index 4a0e50bf..6a7342fc 100644 --- a/tests/R-tests/TestDataGeneration.R +++ b/tests/R-tests/TestDataGeneration.R @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# diff --git a/tests/R-tests/TestDataModeling.R b/tests/R-tests/TestDataModeling.R index b60e935e..d74fe3e4 100644 --- a/tests/R-tests/TestDataModeling.R +++ b/tests/R-tests/TestDataModeling.R @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# diff --git a/tests/R-tests/TestDataPrediction.R b/tests/R-tests/TestDataPrediction.R index 7722a143..9edf61ce 100644 --- a/tests/R-tests/TestDataPrediction.R +++ b/tests/R-tests/TestDataPrediction.R @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# diff --git a/tests/R-tests/TestExaGeoStatAPI.R b/tests/R-tests/TestExaGeoStatAPI.R index a02dda02..b698081d 100644 --- a/tests/R-tests/TestExaGeoStatAPI.R +++ b/tests/R-tests/TestExaGeoStatAPI.R @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST)# diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index 0dffd0c3..9a679a3d 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/Rcpp-adapters/CMakeLists.txt b/tests/cpp-tests/Rcpp-adapters/CMakeLists.txt index 1be993fa..9a4a59a5 100644 --- a/tests/cpp-tests/Rcpp-adapters/CMakeLists.txt +++ b/tests/cpp-tests/Rcpp-adapters/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp index 4f8b22f7..38f193e3 100644 --- a/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp +++ b/tests/cpp-tests/Rcpp-adapters/TestAllRFunctions.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/api/CMakeLists.txt b/tests/cpp-tests/api/CMakeLists.txt index 4d46e039..4c8b2eeb 100644 --- a/tests/cpp-tests/api/CMakeLists.txt +++ b/tests/cpp-tests/api/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/api/TestExaGeoStatApi.cpp b/tests/cpp-tests/api/TestExaGeoStatApi.cpp index db707013..5edcf550 100644 --- a/tests/cpp-tests/api/TestExaGeoStatApi.cpp +++ b/tests/cpp-tests/api/TestExaGeoStatApi.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/configurations/CMakeLists.txt b/tests/cpp-tests/configurations/CMakeLists.txt index 9b7867f7..9b9999e2 100644 --- a/tests/cpp-tests/configurations/CMakeLists.txt +++ b/tests/cpp-tests/configurations/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/configurations/TestConfigurations.cpp b/tests/cpp-tests/configurations/TestConfigurations.cpp index 76a2e715..6ea294f6 100644 --- a/tests/cpp-tests/configurations/TestConfigurations.cpp +++ b/tests/cpp-tests/configurations/TestConfigurations.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/data-generators/CMakeLists.txt b/tests/cpp-tests/data-generators/CMakeLists.txt index df341db5..c84e7cfd 100644 --- a/tests/cpp-tests/data-generators/CMakeLists.txt +++ b/tests/cpp-tests/data-generators/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp index 4fea5441..8585b837 100644 --- a/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestCSVDataGenerator.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp index a8c540f5..25e07650 100644 --- a/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp +++ b/tests/cpp-tests/data-generators/concrete/TestSyntheticGenerator.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/data-units/CMakeLists.txt b/tests/cpp-tests/data-units/CMakeLists.txt index e08ed3ec..1bbfb1df 100644 --- a/tests/cpp-tests/data-units/CMakeLists.txt +++ b/tests/cpp-tests/data-units/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/data-units/TestDescriptorData.cpp b/tests/cpp-tests/data-units/TestDescriptorData.cpp index 3348d5ac..2b8e97dc 100644 --- a/tests/cpp-tests/data-units/TestDescriptorData.cpp +++ b/tests/cpp-tests/data-units/TestDescriptorData.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/hardware/CMakeLists.txt b/tests/cpp-tests/hardware/CMakeLists.txt index a2812a74..c60a7043 100644 --- a/tests/cpp-tests/hardware/CMakeLists.txt +++ b/tests/cpp-tests/hardware/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp b/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp index a4b50332..67e0e2a3 100644 --- a/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp +++ b/tests/cpp-tests/hardware/TestExaGeoStatHardware.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/helpers/CMakeLists.txt b/tests/cpp-tests/helpers/CMakeLists.txt index cea80b5c..c3f0158c 100644 --- a/tests/cpp-tests/helpers/CMakeLists.txt +++ b/tests/cpp-tests/helpers/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/helpers/TestDiskWriter.cpp b/tests/cpp-tests/helpers/TestDiskWriter.cpp index 85884430..2ffc65dc 100644 --- a/tests/cpp-tests/helpers/TestDiskWriter.cpp +++ b/tests/cpp-tests/helpers/TestDiskWriter.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp b/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp index c4d9caab..aae67a88 100644 --- a/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp +++ b/tests/cpp-tests/helpers/TestDistanceCalculationHelpers.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/CMakeLists.txt b/tests/cpp-tests/kernels/CMakeLists.txt index 21d77dc5..40ea4ea0 100644 --- a/tests/cpp-tests/kernels/CMakeLists.txt +++ b/tests/cpp-tests/kernels/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp index c03d91af..0189e7c5 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternFlexible.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp index 14fe5c84..fd951ab6 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateMaternParsimonious.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp index 09e2ecab..c03aa2ff 100644 --- a/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestBivariateSpacetimeMaternStationary.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp index 3cde4758..c412e4db 100644 --- a/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp +++ b/tests/cpp-tests/kernels/concrete/TestTrivariateMaternParsimonious.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp index c1697056..984f33be 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateExpNonGaussian.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp index 4a5ce9ac..43fba252 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDbeta.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp index c6e0aed0..76e5cdea 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaBeta.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp index 5be19562..3d7e03d4 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdbetaNu.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp index 98659420..762208e4 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdnuNu.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp index 987c3d24..775847ad 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquare.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp index 96541419..657e1357 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareBeta.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp index 1fe1438e..785eb847 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDdsigmaSquareNu.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp index c9d324fa..cdfbbf2c 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDnu.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp index 7aaf6115..bb44c9bc 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternDsigmaSquare.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp index 25b058f3..7252175e 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNonGaussian.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp index 5341f3c6..28bc9221 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternNuggetsStationary.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp index d9c25231..c74cc623 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateMaternStationary.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp index 7f9bf8a8..e6c65c16 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariatePowExpStationary.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp index acb8733c..132d726f 100644 --- a/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp +++ b/tests/cpp-tests/kernels/concrete/TestUnivariateSpacetimeMaternStationary.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt b/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt index 93440ee8..a50eb717 100644 --- a/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt +++ b/tests/cpp-tests/linear-algebra-solvers/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp index 564d6cab..9b31bbfb 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDST.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp index 83e5bfb7..d0d978a2 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestChameleonImplementationDense.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp index caed6e9a..db014810 100644 --- a/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp +++ b/tests/cpp-tests/linear-algebra-solvers/concrete/TestHiCMAImplementationTLR.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/prediction/CMakeLists.txt b/tests/cpp-tests/prediction/CMakeLists.txt index aa7af831..f2d68617 100644 --- a/tests/cpp-tests/prediction/CMakeLists.txt +++ b/tests/cpp-tests/prediction/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/prediction/TestPrediction.cpp b/tests/cpp-tests/prediction/TestPrediction.cpp index dbafbbb1..49605ed7 100644 --- a/tests/cpp-tests/prediction/TestPrediction.cpp +++ b/tests/cpp-tests/prediction/TestPrediction.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/prediction/TestPredictionHelpers.cpp b/tests/cpp-tests/prediction/TestPredictionHelpers.cpp index 99acc5e8..b16c9f6d 100644 --- a/tests/cpp-tests/prediction/TestPredictionHelpers.cpp +++ b/tests/cpp-tests/prediction/TestPredictionHelpers.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/results/CMakeLists.txt b/tests/cpp-tests/results/CMakeLists.txt index 2f9216be..03659365 100644 --- a/tests/cpp-tests/results/CMakeLists.txt +++ b/tests/cpp-tests/results/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/cpp-tests/results/TestResults.cpp b/tests/cpp-tests/results/TestResults.cpp index c77ae811..a5693222 100644 --- a/tests/cpp-tests/results/TestResults.cpp +++ b/tests/cpp-tests/results/TestResults.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/heavy-tests/CMakeLists.txt b/tests/heavy-tests/CMakeLists.txt index f827077c..2764f22b 100644 --- a/tests/heavy-tests/CMakeLists.txt +++ b/tests/heavy-tests/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +# Copyright (c) 2017-2024 King Abdullah University of Science and Technology, # All rights reserved. # ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/heavy-tests/ExamplesTests.cpp b/tests/heavy-tests/ExamplesTests.cpp index 843965c0..1cb91890 100644 --- a/tests/heavy-tests/ExamplesTests.cpp +++ b/tests/heavy-tests/ExamplesTests.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/heavy-tests/HeavyTests.cpp b/tests/heavy-tests/HeavyTests.cpp index 6d2f68f1..61316f7a 100644 --- a/tests/heavy-tests/HeavyTests.cpp +++ b/tests/heavy-tests/HeavyTests.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2017-2023 King Abdullah University of Science and Technology, +// Copyright (c) 2017-2024 King Abdullah University of Science and Technology, // All rights reserved. // ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). diff --git a/tests/heavy-tests/README.md b/tests/heavy-tests/README.md index a880bd46..aca3623a 100644 --- a/tests/heavy-tests/README.md +++ b/tests/heavy-tests/README.md @@ -1,4 +1,3 @@ # Heavy Tests Subdirectory -This directory encompasses all the comprehensive tests for the project. It executes all project computations utilizing a -range of technologies to guarantee the seamless functionality of every aspect. \ No newline at end of file +This directory includes all the heavy tests for the project. It executes all project testing examples using a different set of configurations to guarantee the seamless functionality of every test. \ No newline at end of file From 00fafed9008429d39f259e621bcb625e591cec39 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 21 Apr 2024 18:14:48 +0200 Subject: [PATCH 54/82] resolve threads --- CHANGELOG.md | 8 ++++---- CMakeLists.txt | 14 +++++++------- README.md | 2 +- USER_MANUAL.md | 5 +++-- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a9fd42a..0353252f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] - Version 1.1.0 (YYYY-MM-DD) +## [1.1.0](https://github.com/ecrc/ExaGeoStatCPP/releases/tag/1.1.0) - 2024-04-25 ### Added - Implemented a new changelog. - Introduced a benchmarking script. @@ -32,9 +32,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Cleaned the code base for better readability. ### Removed -- Eliminated non-stationary kernel support. -- Removed Find OpenMP, LAPACKPP, and CuSOLVER. +- Eliminated The non-stationary kernel support. +- Removed FindOpenMP.cmake, FindLAPACKPP.cmake, and FindCuSOLVER.cmake. -## [1.0.0] - 2023-11-12 +## [1.0.0](https://github.com/ecrc/ExaGeoStatCPP/releases/tag/1.0.0) - 2023-11-12 ### Added - Integrated all features present in [ExaGeoStat C version](https://github.com/ecrc/exageostat). diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e9a3cc4..ae680a39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,7 +63,7 @@ enable_language(CXX) # Get the current path of the project. add_compile_definitions(PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}/") -# ExaGeoStatCPP depends on a CUDA +# ExaGeoStatCPP depends on CUDA # ------------------------------- if (USE_CUDA) message("-- Build CUDA Support") @@ -73,7 +73,7 @@ else () unset(BLA_VENDOR) endif () -# ExaGeoStatCPP depends on a MPI +# ExaGeoStatCPP depends on MPI # ------------------------------- if (USE_MPI) message("") @@ -102,21 +102,21 @@ include_directories(${LAPACKE_INCLUDE_DIRS}) # Print installation path of ExaGeoStatCPP. message(STATUS "Installation path : ${CMAKE_INSTALL_PREFIX}") -# ExaGeoStatCPP depends on a HWLoc +# ExaGeoStatCPP depends on HWLoc # ------------------------------- include(ImportHwloc) list(APPEND STARPU_COMPONENT_LIST "HWLOC") string(REPLACE ";" " " STARPU_COMPONENT_STRING "${STARPU_COMPONENT_LIST}") -# ExaGeoStatCPP depends on a StarPu runtime +# ExaGeoStatCPP depends on StarPU runtime # ------------------------------- include(ImportStarPu) -# ExaGeoStatCPP depends on a GSL +# ExaGeoStatCPP depends on GSL # ------------------------------- include(ImportGSL) -# ExaGeoStatCPP depends on a NLOPT +# ExaGeoStatCPP depends on NLOPT # ------------------------------- include(ImportNLOPT) @@ -133,7 +133,7 @@ endif () # ------------------------------- include(ImportChameleon) -# ExaGeoStatCPP depends on a LAPACK/BLASPP +# ExaGeoStatCPP depends on LAPACK/BLASPP # ------------------------------- include(ImportBLASPP) include(ImportLapack) diff --git a/README.md b/README.md index ad84f4da..31cda984 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ ExaGeoStatCPP. R CMD INSTALL . --configure-args="-r" ``` -> For more detailed information on setting up ExaGeoStat with different configurations and enabling technologies such as CUDA, MPI, R, etc., please refer to the [User Manual](USER_MANUAL.md) +> For more detailed information on installing ExaGeoStat with different configurations and enabling technologies such as CUDA, MPI, R, etc., please refer to the [User Manual](USER_MANUAL.md) ## Usage diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 9ba2617f..af78388d 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -15,8 +15,8 @@ > Current Version of ExaGeoStatCPP: 1.1.0 ### Supported Operations: 1. (Data Generation): Generating large geospatial synthetic datasets using dense, Diagonal Super-Tile (DST) and Tile Low-Rank (TLR) approximation techniques. -2. (Data Modeling): Modeling large geospatial datasets on dense, Diagonal Super-Tile (DST) and Tile Low-Rank (TLR) approximation techniques through the Maximum likelihood Estimation (MLE) operation. -3. (Data Prediction): Predicting missing measurements on given locations using dense, Diagonal Super-Tile (DST), and Tile Low-Rank (TLR) approximation techniques. +2. (Data Modeling): Modeling large geospatial datasets on dense, DST and TLR approximation techniques through the Maximum likelihood Estimation (MLE) operation. +3. (Data Prediction): Predicting missing measurements on given locations using dense, DST, and TLR approximation techniques. 4. (MLOE/MMOM): Computing the Mean Loss of Efficiency (MLOE), Mean Misspecification of the Mean Square Error (MMOM), and Root mean square MOM (RMOM) to describe the prediction performance over the whole observation region. 5. (Fisher Information Matrix (FIM)): Quantifying the information content that a variable x carries about a parameter $\theta$ within a Gaussian distribution. @@ -452,6 +452,7 @@ idw_error = idw(train_data=list(locations_x, locations_y, z_value), test_data=li ## Manuals - Find a detailed Manual for R functions in [ExaGeoStatCPP-R-Interface-Manual](docs/ExaGeoStat-R-Interface-Manual.pdf) - Find a detailed Manual for C++ functions in [ExaGeoStatC-CPP-Manual](docs/ExaGeoStat-CPP-Manual.pdf) +- Doxygen Manual: https://ecrc.github.io/ExaGeoStatCPP ## Contributing [Contribution Guidelines](CONTRIBUTING.md) From 23c4c5f82b75140a49cab0adc326145692be24b4 Mon Sep 17 00:00:00 2001 From: mahmoudElkarargyBS Date: Sun, 21 Apr 2024 21:16:37 +0300 Subject: [PATCH 55/82] fix MPI --- src/hardware/ExaGeoStatHardware.cpp | 4 ++-- src/runtime/starpu/concrete/ddotp-codelet.cpp | 6 +++--- src/runtime/starpu/concrete/dmdet-codelet.cpp | 6 +++--- src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp | 12 ++++++------ src/runtime/starpu/concrete/dmse-codelet.cpp | 8 ++++---- src/runtime/starpu/concrete/dtrace-codelet.cpp | 6 +++--- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 0b7de213..2f8da8ee 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -36,7 +36,6 @@ ExaGeoStatHardware::ExaGeoStatHardware(const std::string &aComputation, const in void ExaGeoStatHardware::InitHardware(const Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber) { - LOGGER("** Initialize ExaGeoStat hardware **") int tag_width = 31, tag_sep = 26; // Init hardware using Chameleon @@ -59,6 +58,7 @@ void ExaGeoStatHardware::InitHardware(const Computation &aComputation, const int #endif } exageostat::helpers::CommunicatorMPI::GetInstance()->SetHardwareInitialization(); + LOGGER("** Initialize ExaGeoStat hardware **") } void ExaGeoStatHardware::FinalizeHardware() { @@ -108,4 +108,4 @@ void *ExaGeoStatHardware::GetContext(Computation aComputation) { } void *ExaGeoStatHardware::mpChameleonContext = nullptr; -void *ExaGeoStatHardware::mpHicmaContext = nullptr; \ No newline at end of file +void *ExaGeoStatHardware::mpHicmaContext = nullptr; diff --git a/src/runtime/starpu/concrete/ddotp-codelet.cpp b/src/runtime/starpu/concrete/ddotp-codelet.cpp index f9604a8b..1057e8a3 100644 --- a/src/runtime/starpu/concrete/ddotp-codelet.cpp +++ b/src/runtime/starpu/concrete/ddotp-codelet.cpp @@ -33,7 +33,7 @@ struct starpu_codelet DDOTPCodelet::cl_ddotp = { .cuda_flags={(0)}, #endif .nbuffers = 2, - .modes = {STARPU_RW, STARPU_R}, + .modes = {STARPU_W, STARPU_W}, .name = "ddotp" }; @@ -53,8 +53,8 @@ void DDOTPCodelet::InsertTask(void *apDescA, void *apDescProduct) { starpu_insert_task(&this->cl_ddotp, STARPU_VALUE, &rows_num, sizeof(int), - STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr(pDesc_product, 0, 0), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr(pDesc_A, row, 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr(pDesc_product, 0, 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr(pDesc_A, row, 0), 0); } } diff --git a/src/runtime/starpu/concrete/dmdet-codelet.cpp b/src/runtime/starpu/concrete/dmdet-codelet.cpp index 503ebc1d..b89fc204 100644 --- a/src/runtime/starpu/concrete/dmdet-codelet.cpp +++ b/src/runtime/starpu/concrete/dmdet-codelet.cpp @@ -33,7 +33,7 @@ struct starpu_codelet DMDETCodelet::cl_dmdet{ .cuda_flags={(0)}, #endif .nbuffers = 2, - .modes = {STARPU_R, STARPU_RW}, + .modes = {STARPU_W, STARPU_W}, .name = "dmdet" }; @@ -49,9 +49,9 @@ void DMDETCodelet::InsertTask(const Computation &aComputation, void *apDescA, rows_num = row == desc_mt - 1 ? desc_m - row * desc_mb : desc_mb; starpu_insert_task(&this->cl_dmdet, STARPU_VALUE, &rows_num, sizeof(int), - STARPU_R, + STARPU_W, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescA, row, aComputation != TILE_LOW_RANK ? row : 0), - STARPU_RW, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescDet, 0, 0), + STARPU_W, aStarPuHelpers->ExaGeoStatDataGetAddr(apDescDet, 0, 0), 0); } } diff --git a/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp b/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp index 104661f2..e7986d6c 100644 --- a/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp +++ b/src/runtime/starpu/concrete/dmloe-mmom-codelet.cpp @@ -33,7 +33,7 @@ struct starpu_codelet DmloeMmomCodelet::cl_dmloe_mmom = { .cuda_flags={(0)}, #endif .nbuffers = 5, - .modes = {STARPU_R, STARPU_R, STARPU_R, STARPU_RW, STARPU_RW}, + .modes = {STARPU_W, STARPU_W, STARPU_W, STARPU_W, STARPU_W}, .name = "dmloe_mmom" }; @@ -54,15 +54,15 @@ void DmloeMmomCodelet::InsertTask(void *apDescExpr1, void *apDescExpr2, void starpu_insert_task(&this->cl_dmloe_mmom, STARPU_VALUE, &rows_num, sizeof(int), STARPU_VALUE, &cols_num, sizeof(int), - STARPU_R, + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr1, row, col), - STARPU_R, + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr2, row, col), - STARPU_R, + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescExpr3, row, col), - STARPU_RW, + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescMLOE, row, col), - STARPU_RW, + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescMMOM, row, col), 0); } diff --git a/src/runtime/starpu/concrete/dmse-codelet.cpp b/src/runtime/starpu/concrete/dmse-codelet.cpp index 657dbdc9..79941c50 100644 --- a/src/runtime/starpu/concrete/dmse-codelet.cpp +++ b/src/runtime/starpu/concrete/dmse-codelet.cpp @@ -33,7 +33,7 @@ struct starpu_codelet DMSECodelet::cl_dmse = { .cuda_flags={(0)}, #endif .nbuffers = 3, - .modes = {STARPU_RW, STARPU_R, STARPU_R}, + .modes = {STARPU_W, STARPU_W, STARPU_W}, .name = "dmse" }; @@ -47,10 +47,10 @@ void DMSECodelet::InsertTask(void *apDescError, void *apDescZPredict, void *a row == pDesc_Z_predict->mt - 1 ? pDesc_Z_predict->m - row * pDesc_Z_predict->mb : pDesc_Z_predict->mb; starpu_insert_task(&this->cl_dmse, STARPU_VALUE, &rows_num, sizeof(int), - STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescError, 0, 0), - STARPU_R, + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescError, 0, 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZPredict, row, 0), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZMiss, row, 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescZMiss, row, 0), 0); } } diff --git a/src/runtime/starpu/concrete/dtrace-codelet.cpp b/src/runtime/starpu/concrete/dtrace-codelet.cpp index 1572becb..0d840302 100644 --- a/src/runtime/starpu/concrete/dtrace-codelet.cpp +++ b/src/runtime/starpu/concrete/dtrace-codelet.cpp @@ -33,7 +33,7 @@ struct starpu_codelet DTRACECodelet::cl_dtrace = { .cuda_flags={(0)}, #endif .nbuffers = 3, - .modes = {STARPU_R, STARPU_RW, STARPU_W}, + .modes = {STARPU_W, STARPU_W, STARPU_W}, .name = "dtrace" }; @@ -46,8 +46,8 @@ void DTRACECodelet::InsertTask(void *apDescA, void *apDescNum, void *apDescTr rows_num = row == pDescriptor_A->mt - 1 ? pDescriptor_A->m - row * pDescriptor_A->mb : pDescriptor_A->mb; starpu_insert_task(&this->cl_dtrace, STARPU_VALUE, &rows_num, sizeof(int), - STARPU_R, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescA, row, row), - STARPU_RW, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescNum, 0, 0), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescA, row, row), + STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescNum, 0, 0), STARPU_W, (starpu_data_handle_t) RUNTIME_data_getaddr((CHAM_desc_t *) apDescTrace, row, 0), 0); } From 6488a8ac7bfe7761a34bdceb19b81ef0f7c81c2d Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 21 Apr 2024 20:37:16 +0200 Subject: [PATCH 56/82] resolve thread --- USER_MANUAL.md | 2 +- src/runtime/starpu/StarPuFunctions.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/USER_MANUAL.md b/USER_MANUAL.md index af78388d..d181d7ae 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -240,7 +240,7 @@ for setting your arguments: synthetic_data_configurations.SetKernelName("BivariateSpacetimeMaternStationary"); synthetic_data_configurations.SetPrecision(exageostat::common::double); ``` -### Initialize and Finalize the Hardware +### Initialize and Finalize the Hardware class To use any operations, you must initialize the hardware by selecting the number of CPUs and/or GPUs. ```c++ diff --git a/src/runtime/starpu/StarPuFunctions.cpp b/src/runtime/starpu/StarPuFunctions.cpp index bf15d66a..315c1df6 100644 --- a/src/runtime/starpu/StarPuFunctions.cpp +++ b/src/runtime/starpu/StarPuFunctions.cpp @@ -17,7 +17,6 @@ #include #include -using namespace std; using namespace exageostat::common; using namespace exageostat::runtime; using namespace exageostat::dataunits; From 746d87530af426ab1537a7b09a2869af3aaa90be Mon Sep 17 00:00:00 2001 From: mahmoud Date: Mon, 22 Apr 2024 13:56:40 +0200 Subject: [PATCH 57/82] updated R documentation --- DESCRIPTION | 10 +-- docs/ExaGeoStat-R-Interface-Manual.pdf | Bin 119253 -> 117727 bytes man/Data.Rd | 19 +++--- man/Hardware.Rd | 14 +++-- man/fisher.Rd | 82 +++++++++++-------------- man/get_Z_measurement_vector.Rd | 6 +- man/get_locationsX.Rd | 6 +- man/get_locationsY.Rd | 6 +- man/get_locationsZ.Rd | 6 +- man/idw.Rd | 81 +++++++++++------------- man/mloe_mmom.Rd | 81 +++++++++++------------- man/model_data.Rd | 81 +++++++++++------------- man/predict_data.Rd | 81 +++++++++++------------- man/simulate_data.Rd | 72 +++++++++------------- 14 files changed, 241 insertions(+), 304 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 1e6f8fee..7e2313fc 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,16 +1,16 @@ Package: ExaGeoStatCPP Type: Package -Title: R Package Demonstrates the R/C++ Language Interface for Exascale GeoStatistics software +Title: R Package demonstrates the R/C++ Language Interface for Exascale GeoStatistics software (ExaGeoStat) Version: 1.1.0 Date: 2024-01-14 Author: Mahmoud ElKarargy [aut, cph], Sameh Abdulah [cre, cph], KAUST King Abdullah University of Science and Technology [fnd, cph], Brightskies [cph] Maintainer: Sameh Abdulah -Description: An R-Interface for ExaGeoStatCPP: a parallel high performance unified framework for geostatistics on manycore systems. Its abbreviation stands for Exascale Geostatistics. The framework aims at optimizing the likelihood function for a given spatial data to provide an efficient way to predict missing observations. The framework targets many-core systems: clusters of CPUs and GPUs. +Description: An R-Interface for the ExaGeoStatCPP software: a parallel high performance unified framework for geostatistics on manycore systems. Its abbreviation stands for Exascale Geostatistics. The framework aims at optimizing the likelihood function for a given spatial data to provide an efficient way to predict missing observations. The framework targets many-core systems: clusters of CPUs and GPUs. License: GPL (>= 3) Imports: assertthat (>= 0.2.1), MASS, methods, Rcpp (>= 1.0.9) -Depends: R (>= 3.5.0), assertthat (>= 0.2.1), MASS +Depends: R (>= 3.5.0), assertthat (>= 0.2.1) RoxygenNote: 7.2.3 -SystemRequirements: C++ (>= 11), lapacke (https://github.com/xianyi/OpenBLAS/releases) +SystemRequirements: C++ (>= 11), CMake (>= 3.2), lapacke (https://github.com/xianyi/OpenBLAS/releases) NeedsCompilation: yes OS_type: unix Authors@R: c( @@ -19,6 +19,6 @@ Authors@R: c( person("KAUST", "King Abdullah University of Science and Technology", role=c("fnd","cph")), person("Brightskies", role=c("cph")) ) -URL: https://www.github.com/ecrc/ExaGeoStatCPP +URL: https://github.com/ecrc/ExaGeoStatCPP BugReports: https://github.com/ecrc/ExaGeoStatCPP/issues Encoding: UTF-8 \ No newline at end of file diff --git a/docs/ExaGeoStat-R-Interface-Manual.pdf b/docs/ExaGeoStat-R-Interface-Manual.pdf index 12458e1c4f05ab17ae968bab6ea27aa8801f73cf..66b091dc9e53d91dd17e4a474dea725492f6283a 100644 GIT binary patch delta 82277 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

    p! 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 MjJZ@*UH#p-0Q0uGF#rGn diff --git a/man/Data.Rd b/man/Data.Rd index 95be1ec6..11d8f96e 100644 --- a/man/Data.Rd +++ b/man/Data.Rd @@ -14,17 +14,22 @@ \title{ExaGeoStatData Class} \description{ -The ExaGeoStatData class represents a data component in the ExaGeoStat system, that manages geo-statistical data with functions -for location and descriptor manipulation. It is initialized with the size and dimension of the data. +The \code{ExaGeoStatData} class is designed to facilitate the handling and manipulation of geospatial statistics data within the ExaGeoStat framework. +It provides a structured way to store and manage data points based on their dimensions. +Instances of this class can hold a specified number of location points, and support different data dimensions including two-dimensional (2D), three-dimensional (3D), and spatiotemporal (ST) configurations. } \section{Constructor}{ - \code{\link{ExaGeoStatData}} Creates a new instance of the \code{ExaGeoStatData} class. - \code{ExaGeoStatData(size, dimension)} - \describe{ + \code{\link{ExaGeoStatData}} Creates a new instance of the + \code{ExaGeoStatData} class. This is achieved by calling: + \preformatted{ + new(ExaGeoStatData, problem_size, dimension) + } + \describe{ \item{\code{size}}{An integer representing the size of the locations data.} - \item{\code{dimension}}{A string representing the dimensions of the data. - available dimension ("2D", "3D", "ST")} - } + \item{\code{dimension}}{A string representing the dimensions of the data. + Available dimensions are "2D", "3D", and "ST".} + } } \value{ diff --git a/man/Hardware.Rd b/man/Hardware.Rd index 2684d6e2..a1f82d55 100644 --- a/man/Hardware.Rd +++ b/man/Hardware.Rd @@ -19,12 +19,15 @@ It is initialized with computation mode, and two integers representing number of } \section{Constructor}{ - \code{\link{ExaGeoStatHardware}} Creates a new instance of the \code{ExaGeoStatHardware} class. - \code{ExaGeoStatHardware(computation, num_of_cpus, num_of_gpus)} + \code{\link{ExaGeoStatHardware}} Creates a new instance of the + \code{ExaGeoStatHardware} class. This is achieved by calling: + \preformatted{ + new(Hardware, computation, ncores, ngpus) + } \describe{ - \item{\code{computation}}{A string specifying the computation method, either "exact" or "dst" or "tlr".} - \item{\code{num_of_cpus}}{An integer representing number of CPU cores.} - \item{\code{num_of_gpus}}{An integer representing number of GPU cores.} + \item{\code{computation}}{A string specifying the computation method, either "exact" or "dst" or "tlr".} + \item{\code{num_of_cpus}}{An integer representing number of CPU cores.} + \item{\code{num_of_gpus}}{An integer representing number of GPU cores.} } } @@ -43,7 +46,6 @@ ncores <- 2 ngpus <- 0 computation <- "exact" hardware <- new(Hardware, computation, ncores, ngpus) - hardware$finalize_hardware() } diff --git a/man/fisher.Rd b/man/fisher.Rd index d94b582f..60cfa086 100644 --- a/man/fisher.Rd +++ b/man/fisher.Rd @@ -10,54 +10,38 @@ % @date 2024-03-17 \name{fisher} -\alias{fisher} -\title{Compute the Fisher information matrix for a given data and theta vector} +\alias{fisher } +\title{Fisher Function} \usage{ -fisher(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", train_data, test_data) +fisher(kernel, distance_matrix = "euclidean", estimated_theta, dts, +lts = 0, dimension = "2D", train_data, test_data) } \arguments{ -\item{kernel}{A string specifying the kernel to use - available kernels -( "BivariateMaternFlexible", - "BivariateMaternParsimonious", - "BivariateSpacetimeMaternStationary", - "TrivariateMaternParsimonious", - "UnivariateExpNonGaussian", - "UnivariateMaternDbeta", - "UnivariateMaternDdbetaBeta", - "UnivariateMaternDdbetaNu", - "UnivariateMaternDdnuNu", - "UnivariateMaternDdsigmaSquare", - "UnivariateMaternDdsigmaSquareBeta", - "UnivariateMaternDdsigmaSquareNu", - "UnivariateMaternDnu", - "UnivariateMaternDsigmaSquare", - "UnivariateMaternNonGaussian", - "UnivariateMaternNuggetsStationary", - "UnivariateMaternStationary", - "UnivariatePowExpStationary", - "UnivariateSpacetimeMaternStationary", - "bivariate_matern_flexible", - "bivariate_matern_parsimonious", - "bivariate_spacetime_matern_stationary", - "trivariate_matern_parsimonious", - "univariate_exp_non_gaussian", - "univariate_matern_dbeta", - "univariate_matern_ddbeta_beta", - "univariate_matern_ddbeta_nu", - "univariate_matern_ddnu_nu", - "univariate_matern_ddsigma_square", - "univariate_matern_ddsigma_square_beta", - "univariate_matern_ddsigma_square_nu", - "univariate_matern_dnu", - "univariate_matern_dsigma_square", - "univariate_matern_non_gaussian", - "univariate_matern_nuggets_stationary", - "univariate_matern_stationary", - "univariate_pow_exp_stationary", - "univariate_spacetime_matern_stationary" -)} +\item{kernel}{A string specifying the kernel to use. Available kernels include: + \itemize{ + \item "BivariateMaternFlexible" + \item "BivariateMaternParsimonious" + \item "BivariateSpacetimeMaternStationary" + \item "TrivariateMaternParsimonious" + \item "UnivariateExpNonGaussian" + \item "UnivariateMaternDbeta" + \item "UnivariateMaternDdbetaBeta" + \item "UnivariateMaternDdbetaNu" + \item "UnivariateMaternDdnuNu" + \item "UnivariateMaternDdsigmaSquare" + \item "UnivariateMaternDdsigmaSquareBeta" + \item "UnivariateMaternDdsigmaSquareNu" + \item "UnivariateMaternDnu" + \item "UnivariateMaternDsigmaSquare" + \item "UnivariateMaternNonGaussian" + \item "UnivariateMaternNuggetsStationary" + \item "UnivariateMaternStationary" + \item "UnivariatePowExpStationary" + \item "UnivariateSpacetimeMaternStationary" + } + } \item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} \item{estimated_theta}{A list of estimated theta parameters.} \item{dts}{A numeric value representing the time step size.} @@ -83,12 +67,16 @@ computation <- "exact" hardware <- new(Hardware, computation, ncores, ngpus) -z_value <- c(-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) -locations_x <- c(0.092042420080872822, 0.193041886015106440, 0.330556191348134576, 0.181612878614480805) -locations_y <- c(0.928648813611047563, 0.103883421072709245, 0.135790035858701447, 0.434683756771190977) +z_value <- c(-1.272336140360187606, -2.590699695867695773, + 0.512142584178685967, -0.163880452049749520) +locations_x <- c(0.092042420080872822, 0.193041886015106440, + 0.330556191348134576, 0.181612878614480805) +locations_y <- c(0.928648813611047563, 0.103883421072709245, + 0.135790035858701447, 0.434683756771190977) test_x <- c(0.347951, 0.62768) test_y <- c(0.806332, 0.105196) -fisher_matrix <- fisher(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) +fisher_matrix <- fisher(train_data=list(locations_x, locations_y, z_value), +test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) } diff --git a/man/get_Z_measurement_vector.Rd b/man/get_Z_measurement_vector.Rd index c19c42ed..aaeb2398 100644 --- a/man/get_Z_measurement_vector.Rd +++ b/man/get_Z_measurement_vector.Rd @@ -11,7 +11,7 @@ \name{get_Z_measurement_vector} \alias{get_Z_measurement_vector} -\title{Get descriptive Z values from ExaGeoStat data} +\title{Get descriptive Z values Function} \description{ Retrieves descriptive Z values from ExaGeoStat data based on type. @@ -43,7 +43,7 @@ empty_data <- new(Data, problem_size, dimension) dts <- 2 kernel <- "univariate_matern_stationary" initial_theta <- c(1,0.1,0.5) -exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) - +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, +problem_size=problem_size, dts=dts, dimension=dimension) Z <- get_Z_measurement_vector(data=exageostat_data, type="chameleon") } diff --git a/man/get_locationsX.Rd b/man/get_locationsX.Rd index caa777e8..430946ac 100644 --- a/man/get_locationsX.Rd +++ b/man/get_locationsX.Rd @@ -11,7 +11,7 @@ \name{get_locationsX} \alias{get_locationsX} -\title{Get X Locations} +\title{Get Locations X Function} \description{ Retrieves X coordinates of locations from ExaGeoStatData object. @@ -42,7 +42,7 @@ empty_data <- new(Data, problem_size, dimension) dts <- 2 kernel <- "univariate_matern_stationary" initial_theta <- c(1,0.1,0.5) -exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) - +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, +problem_size=problem_size, dts=dts, dimension=dimension) x <- get_locationsX(data=exageostat_data) } diff --git a/man/get_locationsY.Rd b/man/get_locationsY.Rd index a6367b25..3307b57d 100644 --- a/man/get_locationsY.Rd +++ b/man/get_locationsY.Rd @@ -11,7 +11,7 @@ \name{get_locationsY} \alias{get_locationsY} -\title{Get Y Locations} +\title{Get Locations Y Function} \description{ This function retrieves the Y locations from the provided data. @@ -42,7 +42,7 @@ empty_data <- new(Data, problem_size, dimension) dts <- 2 kernel <- "univariate_matern_stationary" initial_theta <- c(1,0.1,0.5) -exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) - +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, +problem_size=problem_size, dts=dts, dimension=dimension) y <- get_locationsY(data=exageostat_data) } diff --git a/man/get_locationsZ.Rd b/man/get_locationsZ.Rd index 412eab04..1effab1f 100644 --- a/man/get_locationsZ.Rd +++ b/man/get_locationsZ.Rd @@ -11,7 +11,7 @@ \name{get_locationsZ} \alias{get_locationsZ} -\title{Get Z Locations} +\title{Get Locations Z Function} \description{ Retrieves Z coordinates of locations from ExaGeoStatData object. @@ -42,7 +42,7 @@ empty_data <- new(Data, problem_size, dimension) dts <- 2 kernel <- "univariate_matern_stationary" initial_theta <- c(1,0.1,0.5) -exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) - +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, +problem_size=problem_size, dts=dts, dimension=dimension) z <- get_locationsZ(data=exageostat_data) } diff --git a/man/idw.Rd b/man/idw.Rd index 82b93e07..5e8b0cea 100644 --- a/man/idw.Rd +++ b/man/idw.Rd @@ -11,53 +11,37 @@ \name{idw} \alias{idw} -\title{This function performs IDW interpolation for a given dataset and theta vector.} +\title{IDW Function} \usage{ -idw(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", train_data, test_data, test_measurements) +idw(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, +dimension = "2D", train_data, test_data, test_measurements) } \arguments{ - \item{kernel}{A string specifying the kernel to use - available kernels - ( "BivariateMaternFlexible", - "BivariateMaternParsimonious", - "BivariateSpacetimeMaternStationary", - "TrivariateMaternParsimonious", - "UnivariateExpNonGaussian", - "UnivariateMaternDbeta", - "UnivariateMaternDdbetaBeta", - "UnivariateMaternDdbetaNu", - "UnivariateMaternDdnuNu", - "UnivariateMaternDdsigmaSquare", - "UnivariateMaternDdsigmaSquareBeta", - "UnivariateMaternDdsigmaSquareNu", - "UnivariateMaternDnu", - "UnivariateMaternDsigmaSquare", - "UnivariateMaternNonGaussian", - "UnivariateMaternNuggetsStationary", - "UnivariateMaternStationary", - "UnivariatePowExpStationary", - "UnivariateSpacetimeMaternStationary", - "bivariate_matern_flexible", - "bivariate_matern_parsimonious", - "bivariate_spacetime_matern_stationary", - "trivariate_matern_parsimonious", - "univariate_exp_non_gaussian", - "univariate_matern_dbeta", - "univariate_matern_ddbeta_beta", - "univariate_matern_ddbeta_nu", - "univariate_matern_ddnu_nu", - "univariate_matern_ddsigma_square", - "univariate_matern_ddsigma_square_beta", - "univariate_matern_ddsigma_square_nu", - "univariate_matern_dnu", - "univariate_matern_dsigma_square", - "univariate_matern_non_gaussian", - "univariate_matern_nuggets_stationary", - "univariate_matern_stationary", - "univariate_pow_exp_stationary", - "univariate_spacetime_matern_stationary" - )} +\item{kernel}{A string specifying the kernel to use. Available kernels include: + \itemize{ + \item "BivariateMaternFlexible" + \item "BivariateMaternParsimonious" + \item "BivariateSpacetimeMaternStationary" + \item "TrivariateMaternParsimonious" + \item "UnivariateExpNonGaussian" + \item "UnivariateMaternDbeta" + \item "UnivariateMaternDdbetaBeta" + \item "UnivariateMaternDdbetaNu" + \item "UnivariateMaternDdnuNu" + \item "UnivariateMaternDdsigmaSquare" + \item "UnivariateMaternDdsigmaSquareBeta" + \item "UnivariateMaternDdsigmaSquareNu" + \item "UnivariateMaternDnu" + \item "UnivariateMaternDsigmaSquare" + \item "UnivariateMaternNonGaussian" + \item "UnivariateMaternNuggetsStationary" + \item "UnivariateMaternStationary" + \item "UnivariatePowExpStationary" + \item "UnivariateSpacetimeMaternStationary" + } + } \item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} \item{estimated_theta}{A list of estimated theta parameters} \item{dts}{A numeric value representing the time step size} @@ -88,12 +72,17 @@ dts <- 2 kernel <- "univariate_matern_stationary" estimated_theta <- c(1,0.1,0.5) -z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) -locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) -locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520) +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440) +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537) test_x <- c(0.347951, 0.62768) test_y <- c(0.806332, 0.105196) test_measurements = c(-1.05428, -1.47441) -idw_error = idw(kernel=kernel, estimated_theta=estimated_theta, dts=dts, train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), test_measurements=test_measurements) +idw_error = idw(kernel=kernel, estimated_theta=estimated_theta, dts=dts, +train_data=list(locations_x, locations_y, z_value), +test_data=list(test_x, test_y), test_measurements=test_measurements) } diff --git a/man/mloe_mmom.Rd b/man/mloe_mmom.Rd index 5cf8e739..7a53a027 100644 --- a/man/mloe_mmom.Rd +++ b/man/mloe_mmom.Rd @@ -11,53 +11,37 @@ \name{mloe_mmom} \alias{mloe_mmom} -\title{Mean Misspecification of the Mean Square Error (MMOM) and Mean Loss of Efficiency (MLOE) using exact method.} +\title{MLOE MMOM Function} \usage{ -mloe_mmom(kernel, distance_matrix="euclidean", estimated_theta, true_theta, dts, lts=0, dimension="2D", train_data, test_data) +mloe_mmom(kernel, distance_matrix="euclidean", estimated_theta, true_theta, +dts, lts=0, dimension="2D", train_data, test_data) } \arguments{ - \item{kernel}{A string specifying the kernel to use - available kernels - ( "BivariateMaternFlexible", - "BivariateMaternParsimonious", - "BivariateSpacetimeMaternStationary", - "TrivariateMaternParsimonious", - "UnivariateExpNonGaussian", - "UnivariateMaternDbeta", - "UnivariateMaternDdbetaBeta", - "UnivariateMaternDdbetaNu", - "UnivariateMaternDdnuNu", - "UnivariateMaternDdsigmaSquare", - "UnivariateMaternDdsigmaSquareBeta", - "UnivariateMaternDdsigmaSquareNu", - "UnivariateMaternDnu", - "UnivariateMaternDsigmaSquare", - "UnivariateMaternNonGaussian", - "UnivariateMaternNuggetsStationary", - "UnivariateMaternStationary", - "UnivariatePowExpStationary", - "UnivariateSpacetimeMaternStationary", - "bivariate_matern_flexible", - "bivariate_matern_parsimonious", - "bivariate_spacetime_matern_stationary", - "trivariate_matern_parsimonious", - "univariate_exp_non_gaussian", - "univariate_matern_dbeta", - "univariate_matern_ddbeta_beta", - "univariate_matern_ddbeta_nu", - "univariate_matern_ddnu_nu", - "univariate_matern_ddsigma_square", - "univariate_matern_ddsigma_square_beta", - "univariate_matern_ddsigma_square_nu", - "univariate_matern_dnu", - "univariate_matern_dsigma_square", - "univariate_matern_non_gaussian", - "univariate_matern_nuggets_stationary", - "univariate_matern_stationary", - "univariate_pow_exp_stationary", - "univariate_spacetime_matern_stationary" - )} + \item{kernel}{A string specifying the kernel to use. Available kernels include: + \itemize{ + \item "BivariateMaternFlexible" + \item "BivariateMaternParsimonious" + \item "BivariateSpacetimeMaternStationary" + \item "TrivariateMaternParsimonious" + \item "UnivariateExpNonGaussian" + \item "UnivariateMaternDbeta" + \item "UnivariateMaternDdbetaBeta" + \item "UnivariateMaternDdbetaNu" + \item "UnivariateMaternDdnuNu" + \item "UnivariateMaternDdsigmaSquare" + \item "UnivariateMaternDdsigmaSquareBeta" + \item "UnivariateMaternDdsigmaSquareNu" + \item "UnivariateMaternDnu" + \item "UnivariateMaternDsigmaSquare" + \item "UnivariateMaternNonGaussian" + \item "UnivariateMaternNuggetsStationary" + \item "UnivariateMaternStationary" + \item "UnivariatePowExpStationary" + \item "UnivariateSpacetimeMaternStationary" + } + } \item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} \item{estimated_theta}{A list of estimated theta parameters} \item{true_theta}{A list of truth theta parameters} @@ -89,12 +73,17 @@ kernel <- "univariate_matern_stationary" estimated_theta <- c(1,0.1,0.5) true_theta <- c(1.1,0.2,0.5) -z_value <- c(-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) -locations_x <- c(0.092042420080872822, 0.193041886015106440, 0.330556191348134576, 0.181612878614480805) -locations_y <- c(0.928648813611047563, 0.103883421072709245, 0.135790035858701447, 0.434683756771190977) +z_value <- c(-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520) +locations_x <- c(0.092042420080872822, 0.193041886015106440, 0.330556191348134576, + 0.181612878614480805) +locations_y <- c(0.928648813611047563, 0.103883421072709245, 0.135790035858701447, + 0.434683756771190977) test_x <- c(0.347951, 0.62768) test_y <- c(0.806332, 0.105196) -result_mloe_mmom = mloe_mmom(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta, true_theta=true_theta) +result_mloe_mmom = mloe_mmom(train_data=list(locations_x, locations_y, z_value), +test_data=list(test_x, test_y), kernel=kernel, dts=dts, +estimated_theta=estimated_theta, true_theta=true_theta) } diff --git a/man/model_data.Rd b/man/model_data.Rd index 645656d4..c5de6124 100644 --- a/man/model_data.Rd +++ b/man/model_data.Rd @@ -11,54 +11,39 @@ \name{model_data} \alias{model_data} -\title{This function models data based on the provided computation method, kernel, distance matrix, and other parameters.} +\title{Model Data Function} \usage{ -model_data(computation = "exact", kernel, distance_matrix = "euclidean", lb, ub, tol = 4, mle_itr, dts, lts = 0, dimension = "2D", band = 0, max_rank = 500, data = NULL, matrix = NULL, x = NULL, y = NULL, z = NULL) +model_data(computation = "exact", kernel, distance_matrix = "euclidean", lb, +ub, tol = 4, mle_itr, dts, lts = 0, dimension = "2D", band = 0, max_rank = 500, +data = NULL, matrix = NULL, x = NULL, y = NULL, z = NULL) } \arguments{ \item{computation}{A string specifying the computation method, either "exact" or "dst" or "tlr". Default is "exact".} -\item{kernel}{A string specifying the kernel to use - available kernels - ( "BivariateMaternFlexible", - "BivariateMaternParsimonious", - "BivariateSpacetimeMaternStationary", - "TrivariateMaternParsimonious", - "UnivariateExpNonGaussian", - "UnivariateMaternDbeta", - "UnivariateMaternDdbetaBeta", - "UnivariateMaternDdbetaNu", - "UnivariateMaternDdnuNu", - "UnivariateMaternDdsigmaSquare", - "UnivariateMaternDdsigmaSquareBeta", - "UnivariateMaternDdsigmaSquareNu", - "UnivariateMaternDnu", - "UnivariateMaternDsigmaSquare", - "UnivariateMaternNonGaussian", - "UnivariateMaternNuggetsStationary", - "UnivariateMaternStationary", - "UnivariatePowExpStationary", - "UnivariateSpacetimeMaternStationary", - "bivariate_matern_flexible", - "bivariate_matern_parsimonious", - "bivariate_spacetime_matern_stationary", - "trivariate_matern_parsimonious", - "univariate_exp_non_gaussian", - "univariate_matern_dbeta", - "univariate_matern_ddbeta_beta", - "univariate_matern_ddbeta_nu", - "univariate_matern_ddnu_nu", - "univariate_matern_ddsigma_square", - "univariate_matern_ddsigma_square_beta", - "univariate_matern_ddsigma_square_nu", - "univariate_matern_dnu", - "univariate_matern_dsigma_square", - "univariate_matern_non_gaussian", - "univariate_matern_nuggets_stationary", - "univariate_matern_stationary", - "univariate_pow_exp_stationary", - "univariate_spacetime_matern_stationary" - )} + \item{kernel}{A string specifying the kernel to use. Available kernels include: + \itemize{ + \item "BivariateMaternFlexible" + \item "BivariateMaternParsimonious" + \item "BivariateSpacetimeMaternStationary" + \item "TrivariateMaternParsimonious" + \item "UnivariateExpNonGaussian" + \item "UnivariateMaternDbeta" + \item "UnivariateMaternDdbetaBeta" + \item "UnivariateMaternDdbetaNu" + \item "UnivariateMaternDdnuNu" + \item "UnivariateMaternDdsigmaSquare" + \item "UnivariateMaternDdsigmaSquareBeta" + \item "UnivariateMaternDdsigmaSquareNu" + \item "UnivariateMaternDnu" + \item "UnivariateMaternDsigmaSquare" + \item "UnivariateMaternNonGaussian" + \item "UnivariateMaternNuggetsStationary" + \item "UnivariateMaternStationary" + \item "UnivariatePowExpStationary" + \item "UnivariateSpacetimeMaternStationary" + } + } \item{distance_matrix}{A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} \item{lb}{A numeric value representing the lower bound for the computation.} \item{ub}{A numeric value representing the upper bound for the computation.} @@ -99,9 +84,13 @@ kernel <- "univariate_matern_stationary" lower_bound <- c(0.1,0.1,0.1) upper_bound <- c(5,5,5) -z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) -locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) -locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520) +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440) +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537) -theta <- model_data(kernel=kernel, lb=lower_bound, ub=upper_bound, mle_itr=10, dts=dts, matrix=z_value, x=locations_x, y=locations_y) +theta <- model_data(kernel=kernel, lb=lower_bound, ub=upper_bound, +mle_itr=10, dts=dts, matrix=z_value, x=locations_x, y=locations_y) } diff --git a/man/predict_data.Rd b/man/predict_data.Rd index 50cb490a..a7d3dc36 100644 --- a/man/predict_data.Rd +++ b/man/predict_data.Rd @@ -11,53 +11,37 @@ \name{predict_data} \alias{predict_data} -\title{This function predicts data based on the provided kernel, distance matrix, estimated theta, and other parameters.} +\title{Predict Data Function} \usage{ -predict_data(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, dimension = "2D", train_data, test_data) +predict_data(kernel, distance_matrix = "euclidean", estimated_theta, +dts, lts = 0, dimension = "2D", train_data, test_data) } \arguments{ - \item{kernel}{A string specifying the kernel to use - available kernels - ( "BivariateMaternFlexible", - "BivariateMaternParsimonious", - "BivariateSpacetimeMaternStationary", - "TrivariateMaternParsimonious", - "UnivariateExpNonGaussian", - "UnivariateMaternDbeta", - "UnivariateMaternDdbetaBeta", - "UnivariateMaternDdbetaNu", - "UnivariateMaternDdnuNu", - "UnivariateMaternDdsigmaSquare", - "UnivariateMaternDdsigmaSquareBeta", - "UnivariateMaternDdsigmaSquareNu", - "UnivariateMaternDnu", - "UnivariateMaternDsigmaSquare", - "UnivariateMaternNonGaussian", - "UnivariateMaternNuggetsStationary", - "UnivariateMaternStationary", - "UnivariatePowExpStationary", - "UnivariateSpacetimeMaternStationary", - "bivariate_matern_flexible", - "bivariate_matern_parsimonious", - "bivariate_spacetime_matern_stationary", - "trivariate_matern_parsimonious", - "univariate_exp_non_gaussian", - "univariate_matern_dbeta", - "univariate_matern_ddbeta_beta", - "univariate_matern_ddbeta_nu", - "univariate_matern_ddnu_nu", - "univariate_matern_ddsigma_square", - "univariate_matern_ddsigma_square_beta", - "univariate_matern_ddsigma_square_nu", - "univariate_matern_dnu", - "univariate_matern_dsigma_square", - "univariate_matern_non_gaussian", - "univariate_matern_nuggets_stationary", - "univariate_matern_stationary", - "univariate_pow_exp_stationary", - "univariate_spacetime_matern_stationary" - )} + \item{kernel}{A string specifying the kernel to use. Available kernels include: + \itemize{ + \item "BivariateMaternFlexible" + \item "BivariateMaternParsimonious" + \item "BivariateSpacetimeMaternStationary" + \item "TrivariateMaternParsimonious" + \item "UnivariateExpNonGaussian" + \item "UnivariateMaternDbeta" + \item "UnivariateMaternDdbetaBeta" + \item "UnivariateMaternDdbetaNu" + \item "UnivariateMaternDdnuNu" + \item "UnivariateMaternDdsigmaSquare" + \item "UnivariateMaternDdsigmaSquareBeta" + \item "UnivariateMaternDdsigmaSquareNu" + \item "UnivariateMaternDnu" + \item "UnivariateMaternDsigmaSquare" + \item "UnivariateMaternNonGaussian" + \item "UnivariateMaternNuggetsStationary" + \item "UnivariateMaternStationary" + \item "UnivariatePowExpStationary" + \item "UnivariateSpacetimeMaternStationary" + } + } \item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} \item{estimated_theta}{A list of estimated theta parameters} \item{dts}{A numeric value representing the time step size} @@ -85,11 +69,16 @@ hardware <- new(Hardware, computation, ncores, ngpus) kernel <- "univariate_matern_stationary" estimated_theta <- c(1,0.1,0.5) -z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) -locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, 0.370473792629892440) -locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, 0.400778210116731537) +z_value <- c( -1.272336140360187606, -2.590699695867695773, 0.512142584178685967, + -0.163880452049749520) +locations_x <- c(0.193041886015106440, 0.330556191348134576, 0.181612878614480805, + 0.370473792629892440) +locations_y <- c(0.103883421072709245, 0.135790035858701447, 0.434683756771190977, + 0.400778210116731537) test_x <- c(0.347951, 0.62768) test_y <- c(0.806332, 0.105196) -predict_data(train_data=list(locations_x, locations_y, z_value), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) +predict_data(train_data=list(locations_x, locations_y, z_value), +test_data=list(test_x, test_y), kernel=kernel, dts=dts, +estimated_theta=estimated_theta) } diff --git a/man/simulate_data.Rd b/man/simulate_data.Rd index 229d76e4..364e79ac 100644 --- a/man/simulate_data.Rd +++ b/man/simulate_data.Rd @@ -11,53 +11,38 @@ \name{simulate_data} \alias{simulate_data} -\title{This function simulates data based on the provided computation method, kernel, distance matrix, and other parameters.} +\title{Simulate Data Function} \usage{ -simulate_data(kernel, initial_theta, distance_matrix = "euclidean", problem_size, seed = 0, dts, lts = 0, dimension = "2D", log_path = "", data_path = "", observations_file = "", recovery_file = "") +simulate_data(kernel, initial_theta, distance_matrix = "euclidean", problem_size, +seed = 0, dts, lts = 0, dimension = "2D", log_path = "", data_path = "", +observations_file = "", recovery_file = "") } \arguments{ - \item{kernel}{A string specifying the kernel to use - available kernels - ( "BivariateMaternFlexible", - "BivariateMaternParsimonious", - "BivariateSpacetimeMaternStationary", - "TrivariateMaternParsimonious", - "UnivariateExpNonGaussian", - "UnivariateMaternDbeta", - "UnivariateMaternDdbetaBeta", - "UnivariateMaternDdbetaNu", - "UnivariateMaternDdnuNu", - "UnivariateMaternDdsigmaSquare", - "UnivariateMaternDdsigmaSquareBeta", - "UnivariateMaternDdsigmaSquareNu", - "UnivariateMaternDnu", - "UnivariateMaternDsigmaSquare", - "UnivariateMaternNonGaussian", - "UnivariateMaternNuggetsStationary", - "UnivariateMaternStationary", - "UnivariatePowExpStationary", - "UnivariateSpacetimeMaternStationary", - "bivariate_matern_flexible", - "bivariate_matern_parsimonious", - "bivariate_spacetime_matern_stationary", - "trivariate_matern_parsimonious", - "univariate_exp_non_gaussian", - "univariate_matern_dbeta", - "univariate_matern_ddbeta_beta", - "univariate_matern_ddbeta_nu", - "univariate_matern_ddnu_nu", - "univariate_matern_ddsigma_square", - "univariate_matern_ddsigma_square_beta", - "univariate_matern_ddsigma_square_nu", - "univariate_matern_dnu", - "univariate_matern_dsigma_square", - "univariate_matern_non_gaussian", - "univariate_matern_nuggets_stationary", - "univariate_matern_stationary", - "univariate_pow_exp_stationary", - "univariate_spacetime_matern_stationary" - )} + \item{kernel}{A string specifying the kernel to use. Available kernels include: + \itemize{ + \item "BivariateMaternFlexible" + \item "BivariateMaternParsimonious" + \item "BivariateSpacetimeMaternStationary" + \item "TrivariateMaternParsimonious" + \item "UnivariateExpNonGaussian" + \item "UnivariateMaternDbeta" + \item "UnivariateMaternDdbetaBeta" + \item "UnivariateMaternDdbetaNu" + \item "UnivariateMaternDdnuNu" + \item "UnivariateMaternDdsigmaSquare" + \item "UnivariateMaternDdsigmaSquareBeta" + \item "UnivariateMaternDdsigmaSquareNu" + \item "UnivariateMaternDnu" + \item "UnivariateMaternDsigmaSquare" + \item "UnivariateMaternNonGaussian" + \item "UnivariateMaternNuggetsStationary" + \item "UnivariateMaternStationary" + \item "UnivariatePowExpStationary" + \item "UnivariateSpacetimeMaternStationary" + } + } \item{initial_theta}{A list of initial theta parameters.} \item{distance_matrix}{A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} \item{problem_size}{A numeric value representing the size of the problem to simulate.} @@ -93,5 +78,6 @@ dts <- 2 kernel <- "univariate_matern_stationary" initial_theta <- c(1,0.1,0.5) -exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) +exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, +problem_size=problem_size, dts=dts, dimension=dimension) } From dec9d1a9bfc25363ae673469d9b251c0f8993579 Mon Sep 17 00:00:00 2001 From: mahmoud Date: Sun, 28 Apr 2024 23:00:35 +0300 Subject: [PATCH 58/82] documentation and hardware changes --- DESCRIPTION | 4 +- README.md | 2 +- USER_MANUAL.md | 2 +- cmake/ImportHCore.cmake | 2 +- .../Rcpp-adapters/FunctionsAdapter.hpp | 4 +- inst/include/api/ExaGeoStat.hpp | 4 +- .../include/configurations/Configurations.hpp | 6 +-- inst/include/data-units/DescriptorData.hpp | 2 +- inst/include/hardware/ExaGeoStatHardware.hpp | 52 +++++++++++++++++-- .../concrete/BivariateMaternFlexible.hpp | 2 +- .../concrete/BivariateMaternParsimonious.hpp | 2 +- .../BivariateSpacetimeMaternStationary.hpp | 2 +- .../concrete/TrivariateMaternParsimonious.hpp | 2 +- .../concrete/UnivariateExpNonGaussian.hpp | 2 +- .../concrete/UnivariateMaternDbeta.hpp | 2 +- .../concrete/UnivariateMaternDdbetaBeta.hpp | 2 +- .../concrete/UnivariateMaternDdbetaNu.hpp | 2 +- .../concrete/UnivariateMaternDdnuNu.hpp | 2 +- .../UnivariateMaternDdsigmaSquare.hpp | 2 +- .../UnivariateMaternDdsigmaSquareBeta.hpp | 2 +- .../UnivariateMaternDdsigmaSquareNu.hpp | 2 +- .../kernels/concrete/UnivariateMaternDnu.hpp | 2 +- .../concrete/UnivariateMaternDsigmaSquare.hpp | 2 +- .../concrete/UnivariateMaternNonGaussian.hpp | 2 +- .../UnivariateMaternNuggetsStationary.hpp | 2 +- .../concrete/UnivariateMaternStationary.hpp | 2 +- .../concrete/UnivariatePowExpStationary.hpp | 2 +- .../UnivariateSpacetimeMaternStationary.hpp | 2 +- inst/include/prediction/Prediction.hpp | 8 +-- man/Data.Rd | 8 +-- man/Hardware.Rd | 22 ++++---- man/fisher.Rd | 19 +++---- man/get_Z_measurement_vector.Rd | 10 ++-- man/get_locationsX.Rd | 8 +-- man/get_locationsY.Rd | 6 ++- man/get_locationsZ.Rd | 6 ++- man/idw.Rd | 16 +++--- man/mloe_mmom.Rd | 14 ++--- man/model_data.Rd | 30 +++++------ man/predict_data.Rd | 14 ++--- man/simulate_data.Rd | 26 +++++----- src/Rcpp-adapters/RcppExports.cpp | 2 +- src/Rcpp-adapters/RcppModules.cpp | 2 +- src/hardware/ExaGeoStatHardware.cpp | 37 ++++++++++--- .../LinearAlgebraMethods.cpp | 20 +++---- .../concrete/tlr/HicmaImplementation.cpp | 4 +- tests/R-tests/TestDataGeneration.R | 4 +- tests/R-tests/TestDataModeling.R | 8 +-- tests/R-tests/TestDataPrediction.R | 4 +- tests/R-tests/TestExaGeoStatAPI.R | 4 +- 50 files changed, 237 insertions(+), 151 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 7e2313fc..47ca856f 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: ExaGeoStatCPP Type: Package -Title: R Package demonstrates the R/C++ Language Interface for Exascale GeoStatistics software (ExaGeoStat) +Title: R Package demonstrates the R/C++ Interface for Exascale GeoStatistics software (ExaGeoStat) Version: 1.1.0 Date: 2024-01-14 Author: Mahmoud ElKarargy [aut, cph], Sameh Abdulah [cre, cph], KAUST King Abdullah University of Science and Technology [fnd, cph], Brightskies [cph] @@ -10,7 +10,7 @@ License: GPL (>= 3) Imports: assertthat (>= 0.2.1), MASS, methods, Rcpp (>= 1.0.9) Depends: R (>= 3.5.0), assertthat (>= 0.2.1) RoxygenNote: 7.2.3 -SystemRequirements: C++ (>= 11), CMake (>= 3.2), lapacke (https://github.com/xianyi/OpenBLAS/releases) +SystemRequirements: C++ (>= 11), CMake (>= 3.2), LAPACKE NeedsCompilation: yes OS_type: unix Authors@R: c( diff --git a/README.md b/README.md index 31cda984..66a29c8f 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ int main(int argc, char **argv) { ``` ### R Example: ```R -hardware <- new(Hardware, computation, ncores, ngpus) +hardware <- new(Hardware, computation, ncores, ngpus, p, q) exageostat_data <- simulate_data(kernel=kernel, initial_theta=initial_theta, problem_size=problem_size, dts=dts, dimension=dimension) estimated_theta <- model_data(data=exageostat_data, kernel=kernel, dts=dts, dimension=dimension,lb=lower_bound, ub=upper_bound, mle_itr=10) predict_data(train_data=list(x, y, z_measurement), test_data=list(test_x, test_y), kernel=kernel, dts=dts, estimated_theta=estimated_theta) diff --git a/USER_MANUAL.md b/USER_MANUAL.md index d181d7ae..85b610ed 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -261,7 +261,7 @@ The subsequent arguments are as follows: ##### *ExaGeoStat R Interface* ```R -hardware <- new(Hardware, computation, number of cores, number of gpus); +hardware <- new(Hardware, computation, number of cores, number of gpus, p-grid, q-grid); hardware$finalize_hardware() ``` First arguement represents the name of the R class that wrapps its correponding C++ class, the rest of arguments are the same as the C++ version diff --git a/cmake/ImportHCore.cmake b/cmake/ImportHCore.cmake index 494f26e5..0595ceb1 100644 --- a/cmake/ImportHCore.cmake +++ b/cmake/ImportHCore.cmake @@ -13,7 +13,7 @@ # Configurations for the HCORE library # 'name' is set to "HCORE", serving as the identifier for the dependency throughout this script. set(name "HCORE") -# 'tag' defines the version tag of the HCORE library to be fetched, representing a specific snapshot of the source code in the repository. +# 'tag' defines the version tag of the HCORE library to be fetched, represents a specific snapshot of the source code in the repository. set(tag "v0.1.3") # 'version' indicates the version of the HCORE library, used to ensure compatibility or fulfill certain requirements. set(version "0.1.3") diff --git a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp index f48af464..4a678613 100644 --- a/inst/include/Rcpp-adapters/FunctionsAdapter.hpp +++ b/inst/include/Rcpp-adapters/FunctionsAdapter.hpp @@ -110,7 +110,7 @@ namespace exageostat::adapters { * @param[in] aLocationsX Optional vector of X coordinates for locations, can be nullable. * @param[in] aLocationsY Optional vector of Y coordinates for locations, can be nullable. * @param[in] aLocationsZ Optional vector of Z coordinates for locations, can be nullable. - * @return Vector of doubles representing the modeled theta. + * @return Vector of doubles represents the modeled theta. * */ std::vector R_ExaGeoStatModelData(const std::string &aComputation, const std::string &aKernelName, @@ -181,7 +181,7 @@ namespace exageostat::adapters { * @param[in] aDimension Dimensionality of the spatial data ("2D" or "3D"). * @param[in] aTrainData Training data set used in the model. * @param[in] aTestData Test data set used for validation. - * @return Vector representing the Fisher information matrix. + * @return Vector represents the Fisher information matrix. * */ std::vector R_ExaGeoStatFisher(const std::string &aKernelName, const std::string &aDistanceMatrix, diff --git a/inst/include/api/ExaGeoStat.hpp b/inst/include/api/ExaGeoStat.hpp index 40978215..54c61c16 100644 --- a/inst/include/api/ExaGeoStat.hpp +++ b/inst/include/api/ExaGeoStat.hpp @@ -67,8 +67,8 @@ namespace exageostat::api { * @param[in] aConfigurations Reference to Configurations object containing user input data. * @param[in, out] aData Reference to an ExaGeoStatData object containing needed descriptors, and locations. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. - * @param[in] apTrainLocations (Optional) Pointer to Locations representing training locations. these are used in training phase. - * @param[in] apTestLocations (Optional) Pointer to Locations representing test locations. These are used in prediction phase. + * @param[in] apTrainLocations (Optional) Pointer to Locations represents training locations. these are used in training phase. + * @param[in] apTestLocations (Optional) Pointer to Locations represents test locations. These are used in prediction phase. * @return void * */ diff --git a/inst/include/configurations/Configurations.hpp b/inst/include/configurations/Configurations.hpp index 954d480a..95f5c729 100644 --- a/inst/include/configurations/Configurations.hpp +++ b/inst/include/configurations/Configurations.hpp @@ -319,7 +319,7 @@ namespace exageostat::configurations { /** * @brief Checks the value of the dimension parameter. - * @param[in] aDimension A string representing the dimension. + * @param[in] aDimension A string represents the dimension. * @return The corresponding dimension value. * */ @@ -351,7 +351,7 @@ namespace exageostat::configurations { /** * @brief Checks the value of the unknown observations parameter. - * @param[in] aValue A string representing the number of unknown observations. + * @param[in] aValue A string represents the number of unknown observations. * @return The corresponding integer value. * */ @@ -402,7 +402,7 @@ namespace exageostat::configurations { /** * @brief Checks the run mode and sets the verbosity level. - * @param[in] aVerbosity A string representing the desired run mode ("verbose" or "standard"). + * @param[in] aVerbosity A string represents the desired run mode ("verbose" or "standard"). * @throws std::range_error if the input string is not "verbose" or "standard". * @return void * diff --git a/inst/include/data-units/DescriptorData.hpp b/inst/include/data-units/DescriptorData.hpp index 5d2a9192..e2207cdc 100644 --- a/inst/include/data-units/DescriptorData.hpp +++ b/inst/include/data-units/DescriptorData.hpp @@ -23,7 +23,7 @@ namespace exageostat::dataunits { /** - * @brief Union representing the base descriptor. + * @brief Union represents the base descriptor. * @details This union is used to store different types of descriptors based on the configuration. * */ diff --git a/inst/include/hardware/ExaGeoStatHardware.hpp b/inst/include/hardware/ExaGeoStatHardware.hpp index 40622648..9e760010 100644 --- a/inst/include/hardware/ExaGeoStatHardware.hpp +++ b/inst/include/hardware/ExaGeoStatHardware.hpp @@ -18,7 +18,7 @@ #include /** - * @brief Class representing the hardware configuration for the ExaGeoStat solver. + * @brief Class represents the hardware configuration for the ExaGeoStat solver. * */ class ExaGeoStatHardware { @@ -29,19 +29,24 @@ class ExaGeoStatHardware { * @param[in] aComputation The computation mode for the solver. * @param[in] aCoreNumber The number of CPU cores to use for the solver. * @param[in] aGpuNumber The number of GPUs to use for the solver. + * @param[in] aP The P grid dimension setting, default is 1. + * @param[in] aQ The Q grid dimension setting, default is 1. * */ explicit ExaGeoStatHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, - const int &aGpuNumber); + const int &aGpuNumber, const int &aP = 1, const int &aQ = 1); /** * @brief Constructor for ExaGeoStatHardware. * @param[in] aComputation The computation mode for the solver as a string. * @param[in] aCoreNumber The number of CPU cores to use for the solver. * @param[in] aGpuNumber The number of GPUs to use for the solver. + * @param[in] aP The P grid dimension setting. + * @param[in] aQ The Q grid dimension setting. * */ - explicit ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber); + explicit ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber, + const int &aP = 1, const int &aQ = 1); /** * @brief A Finalize caller for Hardware. @@ -61,11 +66,14 @@ class ExaGeoStatHardware { * @param[in] aComputation The computation mode for the solver. * @param[in] aCoreNumber The number of CPU cores to use for the solver. * @param[in] aGpuNumber The number of GPUs to use for the solver. + * @param[in] aP The P grid dimension setting. + * @param[in] aQ The Q grid dimension setting. * @return void * */ static void - InitHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber); + InitHardware(const exageostat::common::Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber, + const int &aP, const int &aQ); /** * @brief Get the Chameleon hardware context. @@ -89,11 +97,47 @@ class ExaGeoStatHardware { */ [[nodiscard]] static void *GetContext(exageostat::common::Computation aComputation); + /** + * @brief Retrieves the P dimension of the grid. + * @details This function returns the current setting of the P dimension of the grid, which is part of the grid configuration used in various computational processes. + * @return The current P dimension setting. + * + **/ + static int GetPGrid(); + + /** + * @brief Retrieves the Q dimension of the grid. + * @details This function returns the current setting of the Q dimension of the grid, which is part of the grid configuration used in various computational processes. + * @return int The current Q dimension setting. + * + **/ + static int GetQGrid(); + + /** + * @brief Sets the P dimension of the grid. + * @details This function updates the P dimension setting of the grid. This dimension is critical in configuring the grid's layout for simulations or calculations. + * @param[in] aP The new value for the P dimension. + * + **/ + static void SetPGrid(int aP); + + /** + * @brief Sets the Q dimension of the grid. + * @details This function updates the Q dimension setting of the grid. This dimension is crucial in configuring the grid's layout for simulations or calculations. + * @param[in] aQ The new value for the Q dimension. + * + **/ + static void SetQGrid(int aQ); + private: //// Used Pointer to the Chameleon hardware context. static void *mpChameleonContext; //// Used Pointer to the Hicma hardware context. static void *mpHicmaContext; + //// Used P-Grid + static int mPGrid; + //// Used Q-Grid + static int mQGrid; }; #endif // EXAGEOSTATCPP_EXAGEOSTATHARDWARE_HPP \ No newline at end of file diff --git a/inst/include/kernels/concrete/BivariateMaternFlexible.hpp b/inst/include/kernels/concrete/BivariateMaternFlexible.hpp index a9abaa36..73caa332 100644 --- a/inst/include/kernels/concrete/BivariateMaternFlexible.hpp +++ b/inst/include/kernels/concrete/BivariateMaternFlexible.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class BivariateMaternFlexible - * @brief A class representing a Bivariate Matern Flexible kernel. + * @brief A class represents a Bivariate Matern Flexible kernel. * @details This class represents a Bivariate Matern Flexible, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp b/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp index c112fa55..f98b2d32 100644 --- a/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp +++ b/inst/include/kernels/concrete/BivariateMaternParsimonious.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class BivariateMaternParsimonious - * @brief A class representing a Bivariate Matern Parsimonious kernel. + * @brief A class represents a Bivariate Matern Parsimonious kernel. * @details This class represents a Bivariate Matern Parsimonious, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp b/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp index e7e19ca6..89236fae 100644 --- a/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp +++ b/inst/include/kernels/concrete/BivariateSpacetimeMaternStationary.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class BivariateSpacetimeMaternStationary - * @brief A class representing a Bivariate Spacetime Matern Stationary kernel. + * @brief A class represents a Bivariate Spacetime Matern Stationary kernel. * @details This class represents a Bivariate Spacetime Matern Stationary, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp b/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp index b2087646..b3024753 100644 --- a/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp +++ b/inst/include/kernels/concrete/TrivariateMaternParsimonious.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class TrivariateMaternParsimonious - * @brief A class representing a Trivariate Matern Parsimonious kernel. + * @brief A class represents a Trivariate Matern Parsimonious kernel. * @details This class represents a Trivariate Matern Parsimonious, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp b/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp index efd4d6f0..7a51d0db 100644 --- a/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp +++ b/inst/include/kernels/concrete/UnivariateExpNonGaussian.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateExpNonGaussian - * @brief A class representing a Univariate Exp Non Gaussian kernel. + * @brief A class represents a Univariate Exp Non Gaussian kernel. * @details This class represents a Univariate Exp Non Gaussian, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp index 0f3ecaf7..7f51a7eb 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDbeta.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternDbeta - * @brief A class representing a Univariate Matern Dbeta kernel. + * @brief A class represents a Univariate Matern Dbeta kernel. * @details This class represents a Univariate Matern Dbeta, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp index ddc1681b..33cc1dc6 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdbetaBeta.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternDdbetaBeta - * @brief A class representing a Univariate Matern Ddbeta Beta kernel. + * @brief A class represents a Univariate Matern Ddbeta Beta kernel. * @details This class represents a Univariate Matern Ddbeta Beta, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp index 41fe5cc0..06fa2bc0 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdbetaNu.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternDdbetaNu - * @brief A class representing a Univariate Matern Ddbeta Nu kernel. + * @brief A class represents a Univariate Matern Ddbeta Nu kernel. * @details This class represents a Univariate Matern Ddbeta Nu, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp index c5293e95..6d2376a3 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdnuNu.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternDdnuNu - * @brief A class representing a Univariate Matern Ddnu Nu kernel. + * @brief A class represents a Univariate Matern Ddnu Nu kernel. * @details This class represents a Univariate Matern Ddnu Nu , which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp index bc6032e2..9b596972 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquare.hpp @@ -28,7 +28,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternDdsigmaSquare - * @brief A class representing a Univariate Matern Ddsigma Square kernel. + * @brief A class represents a Univariate Matern Ddsigma Square kernel. * @details This class represents a Univariate Matern Ddsigma Square, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp index 047f9029..46c71588 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareBeta.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternDdsigmaSquareBeta - * @brief A class representing a Univariate Matern Ddsigma Square Beta kernel. + * @brief A class represents a Univariate Matern Ddsigma Square Beta kernel. * @details This class represents a Univariate Matern Ddsigma Square Beta, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp index 53fe8f21..5092e4ae 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDdsigmaSquareNu.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternDdsigmaSquareNu - * @brief A class representing a Univariate Matern Ddsigma Square Nu kernel. + * @brief A class represents a Univariate Matern Ddsigma Square Nu kernel. * @details This class represents a Univariate Matern Ddsigma Square Nu, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternDnu.hpp b/inst/include/kernels/concrete/UnivariateMaternDnu.hpp index 2476789c..6c830bc7 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDnu.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDnu.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternDnu - * @brief A class representing a Univariate Matern Dnu kernel. + * @brief A class represents a Univariate Matern Dnu kernel. * @details This class represents a Univariate Matern Dnu, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp b/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp index bf39a24a..d1a3350b 100644 --- a/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternDsigmaSquare.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternDsigmaSquare - * @brief A class representing a Univariate Matern Dsigma Square kernel. + * @brief A class represents a Univariate Matern Dsigma Square kernel. * @details This class represents a Univariate Matern Dsigma Square, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp b/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp index 321913e0..203c4c6e 100644 --- a/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternNonGaussian.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternNonGaussian - * @brief A class representing a Univariate Matern Non Gaussian kernel. + * @brief A class represents a Univariate Matern Non Gaussian kernel. * @details This class represents a Univariate Matern Non Gaussian, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp index b0e5ca91..3503ac44 100644 --- a/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternNuggetsStationary.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternNuggetsStationary - * @brief A class representing a Univariate Matern Nuggets Stationary kernel. + * @brief A class represents a Univariate Matern Nuggets Stationary kernel. * @details This class represents a Univariate Matern Nuggets Stationary, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateMaternStationary.hpp b/inst/include/kernels/concrete/UnivariateMaternStationary.hpp index 8fb3757c..25db2fee 100644 --- a/inst/include/kernels/concrete/UnivariateMaternStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateMaternStationary.hpp @@ -28,7 +28,7 @@ namespace exageostat::kernels { /** * @class UnivariateMaternStationary - * @brief A class representing a Univariate Matern Stationary kernel. + * @brief A class represents a Univariate Matern Stationary kernel. * @details This class represents a Univariate Matern Stationary, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp b/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp index 09939e57..e4a76c4b 100644 --- a/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp +++ b/inst/include/kernels/concrete/UnivariatePowExpStationary.hpp @@ -28,7 +28,7 @@ namespace exageostat::kernels { /** * @class UnivariatePowExpStationary - * @brief A class representing a Univariate PowExp Stationary kernel. + * @brief A class represents a Univariate PowExp Stationary kernel. * @details This class represents a Univariate PowExp Stationary, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp b/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp index 27233ba1..25ef5393 100644 --- a/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp +++ b/inst/include/kernels/concrete/UnivariateSpacetimeMaternStationary.hpp @@ -23,7 +23,7 @@ namespace exageostat::kernels { /** * @class UnivariateSpacetimeMaternStationary - * @brief A class representing a Univariate Spacetime Matern Stationary kernel. + * @brief A class represents a Univariate Spacetime Matern Stationary kernel. * @details This class represents a Univariate Spacetime Matern Stationary, which is a subclass of the Kernel class. * It provides a method for generating a covariance matrix using a set of input locations and kernel parameters. * diff --git a/inst/include/prediction/Prediction.hpp b/inst/include/prediction/Prediction.hpp index 96efb614..280a2396 100644 --- a/inst/include/prediction/Prediction.hpp +++ b/inst/include/prediction/Prediction.hpp @@ -35,8 +35,8 @@ namespace exageostat::prediction { * @param[in] aConfigurations Reference to Configurations object containing user input data. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. * @param[in] aKernel Reference to the kernel object to use. - * @param[in] apTrainLocations (Optional) Pointer to Locations representing training locations. these are used in training phase. - * @param[in] apTestLocations (Optional) Pointer to Locations representing test locations. These are used in prediction phase. + * @param[in] apTrainLocations (Optional) Pointer to Locations represents training locations. these are used in training phase. + * @param[in] apTestLocations (Optional) Pointer to Locations represents test locations. These are used in prediction phase. * @return void * */ @@ -57,8 +57,8 @@ namespace exageostat::prediction { * @param[out] aObsLocation Location object to be filled with missed locations. * @param[in] apMeasurementsMatrix Pointer to the user input measurements matrix. * @param[in] aP the P value of the kernel multiplied by time slot. - * @param[in] apTrainLocations (Optional) Pointer to Locations representing training locations. these are used in training phase. - * @param[in] apTestLocations (Optional) Pointer to Locations representing test locations. These are used in prediction phase. + * @param[in] apTrainLocations (Optional) Pointer to Locations represents training locations. these are used in training phase. + * @param[in] apTestLocations (Optional) Pointer to Locations represents test locations. These are used in prediction phase. * @return void * */ diff --git a/man/Data.Rd b/man/Data.Rd index 11d8f96e..7a42007f 100644 --- a/man/Data.Rd +++ b/man/Data.Rd @@ -21,19 +21,19 @@ Instances of this class can hold a specified number of location points, and supp \section{Constructor}{ \code{\link{ExaGeoStatData}} Creates a new instance of the - \code{ExaGeoStatData} class. This is achieved by calling: + \code{ExaGeoStatData} class by calling: \preformatted{ new(ExaGeoStatData, problem_size, dimension) } \describe{ - \item{\code{size}}{An integer representing the size of the locations data.} - \item{\code{dimension}}{A string representing the dimensions of the data. + \item{\code{size}}{An integer represents the size of the locations data.} + \item{\code{dimension}}{A string represents the dimensions of the data. Available dimensions are "2D", "3D", and "ST".} } } \value{ -An object of class \code{ExaGeoStatData} representing a data component with the specified size and dimension. +An object of class \code{ExaGeoStatData} represents a data component with the specified size and dimension. } \examples{ diff --git a/man/Hardware.Rd b/man/Hardware.Rd index a1f82d55..4b85582f 100644 --- a/man/Hardware.Rd +++ b/man/Hardware.Rd @@ -15,37 +15,41 @@ \description{ The ExaGeoStatHardware class represents a hardware component in the ExaGeoStat system. -It is initialized with computation mode, and two integers representing number of CPU cores and number of GPU cores. +It is initialized with computation mode, and two integers represents number of CPU cores and number of GPU cores. } \section{Constructor}{ \code{\link{ExaGeoStatHardware}} Creates a new instance of the - \code{ExaGeoStatHardware} class. This is achieved by calling: + \code{ExaGeoStatHardware} class by calling: \preformatted{ - new(Hardware, computation, ncores, ngpus) + new(Hardware, computation, ncores, ngpus, p, q) } \describe{ - \item{\code{computation}}{A string specifying the computation method, either "exact" or "dst" or "tlr".} - \item{\code{num_of_cpus}}{An integer representing number of CPU cores.} - \item{\code{num_of_gpus}}{An integer representing number of GPU cores.} + \item{\code{computation}}{A string specifies the computation method, either "exact" or "dst" or "tlr".} + \item{\code{ncores}}{An integer represents number of CPU cores.} + \item{\code{ngpus}}{An integer represents number of GPU cores.} + \item{\code{p}}{An integer represents P grid dimension.} + \item{\code{q}}{An integer represents Q grid dimension.} } } \section{Methods}{ \subsection{finalize_hardware}{ -\code{finalize_hardware()} Manually finalizes the hardware by resetting the context. +\code{finalize_hardware()} manually finalizes the hardware by resetting the context. } } \value{ -An object of class \code{ExaGeoStatHardware} representing a hardware component with the specified component and number of CPU cores and GPU cores. +An object of class \code{ExaGeoStatHardware} represents a hardware component with the specified component and number of CPU cores and GPU cores. } \examples{ ncores <- 2 ngpus <- 0 +p <- 2 +q <- 2 computation <- "exact" -hardware <- new(Hardware, computation, ncores, ngpus) +hardware <- new(Hardware, computation, ncores, ngpus, p, q) hardware$finalize_hardware() } diff --git a/man/fisher.Rd b/man/fisher.Rd index 60cfa086..40e4ab70 100644 --- a/man/fisher.Rd +++ b/man/fisher.Rd @@ -11,7 +11,7 @@ \name{fisher} \alias{fisher } -\title{Fisher Function} +\title{Fisher function} \usage{ fisher(kernel, distance_matrix = "euclidean", estimated_theta, dts, @@ -19,7 +19,7 @@ lts = 0, dimension = "2D", train_data, test_data) } \arguments{ -\item{kernel}{A string specifying the kernel to use. Available kernels include: +\item{kernel}{A string specifies the kernel to use. Available kernels include: \itemize{ \item "BivariateMaternFlexible" \item "BivariateMaternParsimonious" @@ -42,16 +42,16 @@ lts = 0, dimension = "2D", train_data, test_data) \item "UnivariateSpacetimeMaternStationary" } } -\item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} +\item{distance_matrix}{ A string specifies the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} \item{estimated_theta}{A list of estimated theta parameters.} -\item{dts}{A numeric value representing the time step size.} -\item{lts}{A numeric value representing the length step size. Default is 0.} -\item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D".} +\item{dts}{A numeric value represents the time step size.} +\item{lts}{A numeric value represents the length step size. Default is 0.} +\item{dimension}{A string specifies the data dimension, either "2D" or "3D". Default is "2D".} \item{train_data}{ A numeric vector contains the locations and z measurements for training} \item{test_data}{ A numeric vector contains the locations for testing.} } -\value{A vector containing the Fisher information matrix elements.} +\value{A vector contains the Fisher information matrix elements.} \description{This function computes the Fisher information matrix for a given dataset and theta vector, using a specified kernel and distance metric. It also allows for the inclusion of missing values and the specification of data dimensions.} @@ -64,8 +64,9 @@ dts <- 2 kernel <- "univariate_matern_stationary" estimated_theta <- c(1,0.1,0.5) computation <- "exact" - -hardware <- new(Hardware, computation, ncores, ngpus) +p <- 1 +q <- 1 +hardware <- new(Hardware, computation, ncores, ngpus, p, q) z_value <- c(-1.272336140360187606, -2.590699695867695773, 0.512142584178685967, -0.163880452049749520) diff --git a/man/get_Z_measurement_vector.Rd b/man/get_Z_measurement_vector.Rd index aaeb2398..5dc60933 100644 --- a/man/get_Z_measurement_vector.Rd +++ b/man/get_Z_measurement_vector.Rd @@ -1,4 +1,4 @@ - +// TODO: dots % Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). @@ -11,7 +11,7 @@ \name{get_Z_measurement_vector} \alias{get_Z_measurement_vector} -\title{Get descriptive Z values Function} +\title{Get descriptive Z values function} \description{ Retrieves descriptive Z values from ExaGeoStat data based on type. @@ -23,7 +23,7 @@ get_Z_measurement_vector(data,type) \arguments{ \item{data}{A list of ExaGeoStatData that contains the locations.} -\item{type}{A string specifying the type of descriptor value to retrieve (e.g., "Chameleon", "HiCMA").} +\item{type}{A string specifies the type of descriptor value to retrieve (e.g., "Chameleon", "HiCMA").} } \value{ @@ -34,7 +34,9 @@ A numeric vector of descriptive Z values. ncores <- 2 ngpus <- 0 computation <- "exact" -hardware <- new(Hardware, computation, ncores, ngpus) +p <- 1 +q <- 1 +hardware <- new(Hardware, computation, ncores, ngpus, p, q) dimension = "3D" problem_size <- 4 diff --git a/man/get_locationsX.Rd b/man/get_locationsX.Rd index 430946ac..4cc60935 100644 --- a/man/get_locationsX.Rd +++ b/man/get_locationsX.Rd @@ -1,4 +1,4 @@ - +// TODO: Make get locations returns pairs of locations % Copyright (c) 2017-2024 King Abdullah University of Science and Technology, % All rights reserved. % ExaGeoStat is a software package, provided by King Abdullah University of Science and Technology (KAUST). @@ -11,7 +11,7 @@ \name{get_locationsX} \alias{get_locationsX} -\title{Get Locations X Function} +\title{Get Locations X function} \description{ Retrieves X coordinates of locations from ExaGeoStatData object. @@ -33,7 +33,9 @@ A numeric vector of X locations. ncores <- 1 ngpus <- 0 computation <- "exact" -hardware <- new(Hardware, computation, ncores, ngpus) +p <- 1 +q <- 1 +hardware <- new(Hardware, computation, ncores, ngpus, p, q) dimension = "2D" problem_size <- 4 diff --git a/man/get_locationsY.Rd b/man/get_locationsY.Rd index 3307b57d..6f359cd2 100644 --- a/man/get_locationsY.Rd +++ b/man/get_locationsY.Rd @@ -11,7 +11,7 @@ \name{get_locationsY} \alias{get_locationsY} -\title{Get Locations Y Function} +\title{Get Locations Y function} \description{ This function retrieves the Y locations from the provided data. @@ -33,7 +33,9 @@ A numeric vector of Y locations. ncores <- 2 ngpus <- 0 computation <- "exact" -hardware <- new(Hardware, computation, ncores, ngpus) +p <- 1 +q <- 1 +hardware <- new(Hardware, computation, ncores, ngpus, p, q) dimension = "2D" problem_size <- 4 diff --git a/man/get_locationsZ.Rd b/man/get_locationsZ.Rd index 1effab1f..bdaf2175 100644 --- a/man/get_locationsZ.Rd +++ b/man/get_locationsZ.Rd @@ -11,7 +11,7 @@ \name{get_locationsZ} \alias{get_locationsZ} -\title{Get Locations Z Function} +\title{Get Locations Z function} \description{ Retrieves Z coordinates of locations from ExaGeoStatData object. @@ -33,7 +33,9 @@ A numeric vector of Z locations. ncores <- 2 ngpus <- 0 computation <- "exact" -hardware <- new(Hardware, computation, ncores, ngpus) +p <- 1 +q <- 1 +hardware <- new(Hardware, computation, ncores, ngpus, p, q) dimension = "3D" problem_size <- 4 diff --git a/man/idw.Rd b/man/idw.Rd index 5e8b0cea..94c23662 100644 --- a/man/idw.Rd +++ b/man/idw.Rd @@ -11,7 +11,7 @@ \name{idw} \alias{idw} -\title{IDW Function} +\title{IDW function} \usage{ idw(kernel, distance_matrix = "euclidean", estimated_theta, dts, lts = 0, @@ -19,7 +19,7 @@ dimension = "2D", train_data, test_data, test_measurements) } \arguments{ -\item{kernel}{A string specifying the kernel to use. Available kernels include: +\item{kernel}{A string specifies the kernel to use. Available kernels include: \itemize{ \item "BivariateMaternFlexible" \item "BivariateMaternParsimonious" @@ -42,18 +42,18 @@ dimension = "2D", train_data, test_data, test_measurements) \item "UnivariateSpacetimeMaternStationary" } } - \item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} + \item{distance_matrix}{ A string specifies the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} \item{estimated_theta}{A list of estimated theta parameters} - \item{dts}{A numeric value representing the time step size} - \item{lts}{A numeric value representing the length step size. Default is 0} - \item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D"} + \item{dts}{A numeric value represents the time step size} + \item{lts}{A numeric value represents the length step size. Default is 0} + \item{dimension}{A string specifies the data dimension, either "2D" or "3D". Default is "2D"} \item{train_data}{ A numeric vector contains the locations and z measurements for training} \item{test_data}{ A numeric vector contains the locations for testing.} \item{test_measurements}{ A numeric vector contains the z measurements for testing.} } \value{ - A vector containing the IDW error. + A vector contains the IDW error. } \description{ @@ -64,7 +64,7 @@ This function performs Inverse Distance Weighting (IDW) interpolation for a give ncores <- 2 ngpus <- 0 computation <- "exact" -hardware <- new(Hardware, computation, ncores, ngpus) +hardware <- new(Hardware, computation, ncores, ngpus, 1, 1) problem_size <- 4 dimension = "2D" diff --git a/man/mloe_mmom.Rd b/man/mloe_mmom.Rd index 7a53a027..dfbf1504 100644 --- a/man/mloe_mmom.Rd +++ b/man/mloe_mmom.Rd @@ -11,7 +11,7 @@ \name{mloe_mmom} \alias{mloe_mmom} -\title{MLOE MMOM Function} +\title{MLOE MMOM function} \usage{ mloe_mmom(kernel, distance_matrix="euclidean", estimated_theta, true_theta, @@ -19,7 +19,7 @@ dts, lts=0, dimension="2D", train_data, test_data) } \arguments{ - \item{kernel}{A string specifying the kernel to use. Available kernels include: + \item{kernel}{A string specifies the kernel to use. Available kernels include: \itemize{ \item "BivariateMaternFlexible" \item "BivariateMaternParsimonious" @@ -42,12 +42,12 @@ dts, lts=0, dimension="2D", train_data, test_data) \item "UnivariateSpacetimeMaternStationary" } } -\item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} +\item{distance_matrix}{ A string specifies the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} \item{estimated_theta}{A list of estimated theta parameters} \item{true_theta}{A list of truth theta parameters} -\item{dts}{A numeric value representing the time step size} -\item{lts}{A numeric value representing the length step size. Default is 0} -\item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D"} +\item{dts}{A numeric value represents the time step size} +\item{lts}{A numeric value represents the length step size. Default is 0} +\item{dimension}{A string specifies the data dimension, either "2D" or "3D". Default is "2D"} \item{train_data}{ A numeric vector contains the locations and z measurements for training} \item{test_data}{ A numeric vector contains the locations for testing.} } @@ -64,7 +64,7 @@ This function calculates Mean Misspecification of the Mean Square Error (MMOM) a ncores <- 2 ngpus <- 0 computation <- "exact" -hardware <- new(Hardware, computation, ncores, ngpus) +hardware <- new(Hardware, computation, ncores, ngpus, 1, 1) problem_size <- 4 dimension = "2D" diff --git a/man/model_data.Rd b/man/model_data.Rd index c5de6124..b49a3665 100644 --- a/man/model_data.Rd +++ b/man/model_data.Rd @@ -11,7 +11,7 @@ \name{model_data} \alias{model_data} -\title{Model Data Function} +\title{Model Data function} \usage{ model_data(computation = "exact", kernel, distance_matrix = "euclidean", lb, @@ -20,8 +20,8 @@ data = NULL, matrix = NULL, x = NULL, y = NULL, z = NULL) } \arguments{ -\item{computation}{A string specifying the computation method, either "exact" or "dst" or "tlr". Default is "exact".} - \item{kernel}{A string specifying the kernel to use. Available kernels include: +\item{computation}{A string specifies the computation method, either "exact" or "dst" or "tlr". Default is "exact".} + \item{kernel}{A string specifies the kernel to use. Available kernels include: \itemize{ \item "BivariateMaternFlexible" \item "BivariateMaternParsimonious" @@ -44,16 +44,16 @@ data = NULL, matrix = NULL, x = NULL, y = NULL, z = NULL) \item "UnivariateSpacetimeMaternStationary" } } -\item{distance_matrix}{A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} -\item{lb}{A numeric value representing the lower bound for the computation.} -\item{ub}{A numeric value representing the upper bound for the computation.} -\item{tol}{A numeric value specifying the tolerance for the computation. Default is 4.} -\item{mle_itr}{A numeric value specifying the maximum number of iterations for the computation.} -\item{dts}{A numeric value representing the time step size.} -\item{lts}{A numeric value representing the length step size. Default is 0.} -\item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D".} -\item{band}{A numeric value Bandwidth for band matrices, applicable in certain computational kernels.. Default is 0.} -\item{max_rank}{A numeric value specifying the Maximum rank for low-rank approximations.. Default is 500.} +\item{distance_matrix}{A string specifies the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} +\item{lb}{A numeric value represents the lower bound for the computation.} +\item{ub}{A numeric value represents the upper bound for the computation.} +\item{tol}{A numeric value specifies the tolerance for the computation. Default is 4.} +\item{mle_itr}{A numeric value specifies the maximum number of iterations for the computation.} +\item{dts}{A numeric value represents the time step size.} +\item{lts}{A numeric value represents the length step size. Default is 0.} +\item{dimension}{A string specifies the data dimension, either "2D" or "3D". Default is "2D".} +\item{band}{A numeric value Bandwidth for band matrices, applicable in certain computational kernels, Default is 0.} +\item{max_rank}{A numeric value specifies the Maximum rank for low-rank approximations, Default is 500.} \item{data}{A list of data vectors. Default is `R_NilValue`.} \item{matrix}{A matrix object. Default is `R_NilValue`.} \item{x}{A numeric vector. Default is `R_NilValue`.} @@ -62,7 +62,7 @@ data = NULL, matrix = NULL, x = NULL, y = NULL, z = NULL) } \value{ -A vector containing the starting theta. +A vector contains the starting theta. } \description{ @@ -73,7 +73,7 @@ This function models data based on the provided computation method, kernel, dist ncores <- 2 ngpus <- 0 computation <- "exact" -hardware <- new(Hardware, computation, ncores, ngpus) +hardware <- new(Hardware, computation, ncores, ngpus, 1, 1) dimension = "2D" problem_size <- 4 diff --git a/man/predict_data.Rd b/man/predict_data.Rd index a7d3dc36..95ad1859 100644 --- a/man/predict_data.Rd +++ b/man/predict_data.Rd @@ -11,7 +11,7 @@ \name{predict_data} \alias{predict_data} -\title{Predict Data Function} +\title{Predict Data function} \usage{ predict_data(kernel, distance_matrix = "euclidean", estimated_theta, @@ -19,7 +19,7 @@ dts, lts = 0, dimension = "2D", train_data, test_data) } \arguments{ - \item{kernel}{A string specifying the kernel to use. Available kernels include: + \item{kernel}{A string specifies the kernel to use. Available kernels include: \itemize{ \item "BivariateMaternFlexible" \item "BivariateMaternParsimonious" @@ -42,11 +42,11 @@ dts, lts = 0, dimension = "2D", train_data, test_data) \item "UnivariateSpacetimeMaternStationary" } } - \item{distance_matrix}{ A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} + \item{distance_matrix}{ A string specifies the distance metric, either "euclidean" or "great_circle". Default is "euclidean"} \item{estimated_theta}{A list of estimated theta parameters} - \item{dts}{A numeric value representing the time step size} - \item{lts}{A numeric value representing the length step size. Default is 0} - \item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D"} + \item{dts}{A numeric value represents the time step size} + \item{lts}{A numeric value represents the length step size. Default is 0} + \item{dimension}{A string specifies the data dimension, either "2D" or "3D". Default is "2D"} \item{train_data}{ A numeric vector contains the locations and z measurements for training} \item{test_data}{ A numeric vector contains the locations for testing.} } @@ -65,7 +65,7 @@ ngpus <- 0 problem_size <- 4 dts <- 2 computation <- "exact" -hardware <- new(Hardware, computation, ncores, ngpus) +hardware <- new(Hardware, computation, ncores, ngpus, 1, 1) kernel <- "univariate_matern_stationary" estimated_theta <- c(1,0.1,0.5) diff --git a/man/simulate_data.Rd b/man/simulate_data.Rd index 364e79ac..c9f40a2f 100644 --- a/man/simulate_data.Rd +++ b/man/simulate_data.Rd @@ -11,7 +11,7 @@ \name{simulate_data} \alias{simulate_data} -\title{Simulate Data Function} +\title{Simulate Data function} \usage{ simulate_data(kernel, initial_theta, distance_matrix = "euclidean", problem_size, @@ -20,7 +20,7 @@ observations_file = "", recovery_file = "") } \arguments{ - \item{kernel}{A string specifying the kernel to use. Available kernels include: + \item{kernel}{A string specifies the kernel to use. Available kernels include: \itemize{ \item "BivariateMaternFlexible" \item "BivariateMaternParsimonious" @@ -44,16 +44,16 @@ observations_file = "", recovery_file = "") } } \item{initial_theta}{A list of initial theta parameters.} -\item{distance_matrix}{A string specifying the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} -\item{problem_size}{A numeric value representing the size of the problem to simulate.} -\item{seed}{ A numeric value specifying the seed for random number generation. Default is 0.} -\item{dts}{ A numeric value representing the time step size.} -\item{lts}{ A numeric value representing the length step size. Default is 0.} -\item{dimension}{A string specifying the data dimension, either "2D" or "3D". Default is "2D".} -\item{log_path}{A string specifying the path for logging. Default is "".} -\item{data_path}{A string specifying the path for data storage. Default is "".} -\item{observations_file}{A string specifying the file name for observations. Default is "".} -\item{recovery_file}{A string specifying the file name for recovery. Default is "".} +\item{distance_matrix}{A string specifies the distance metric, either "euclidean" or "great_circle". Default is "euclidean".} +\item{problem_size}{A numeric value represents the size of the problem to simulate.} +\item{seed}{ A numeric value specifies the seed for random number generation. Default is 0.} +\item{dts}{ A numeric value represents the time step size.} +\item{lts}{ A numeric value represents the length step size. Default is 0.} +\item{dimension}{A string specifies the data dimension, either "2D" or "3D". Default is "2D".} +\item{log_path}{A string specifies the path for logging.} +\item{data_path}{A string specifies the path for data storage.} +\item{observations_file}{A string specifies the file name for observations.} +\item{recovery_file}{A string specifies the file name for recovery.} } \value{ @@ -68,7 +68,7 @@ This function loads data into an ExaGeoStatData object using the provided config ncores <- 2 ngpus <- 0 computation <- "exact" -hardware <- new(Hardware, computation, ncores, ngpus) +hardware <- new(Hardware, computation, ncores, ngpus, 1, 1) dimension = "2D" problem_size <- 4 diff --git a/src/Rcpp-adapters/RcppExports.cpp b/src/Rcpp-adapters/RcppExports.cpp index 075e5c1f..2b1fbbab 100644 --- a/src/Rcpp-adapters/RcppExports.cpp +++ b/src/Rcpp-adapters/RcppExports.cpp @@ -23,7 +23,7 @@ Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); /** * @brief Rcpp module boot function for ExaGeoStatCPP. - * @return An SEXP representing the Rcpp module. + * @return A SEXP represents the Rcpp module. */ RcppExport SEXP _rcpp_module_boot_ExaGeoStatCPP(); diff --git a/src/Rcpp-adapters/RcppModules.cpp b/src/Rcpp-adapters/RcppModules.cpp index 284a205f..be89e6b4 100644 --- a/src/Rcpp-adapters/RcppModules.cpp +++ b/src/Rcpp-adapters/RcppModules.cpp @@ -32,7 +32,7 @@ RCPP_MODULE(ExaGeoStatCPP) { /** Hardware Class **/ class_("Hardware") - .constructor() + .constructor() .method("finalize_hardware", &ExaGeoStatHardware::FinalizeHardware, "Manually finalize the hardware"); /** Data Class **/ diff --git a/src/hardware/ExaGeoStatHardware.cpp b/src/hardware/ExaGeoStatHardware.cpp index 2f8da8ee..665f956e 100644 --- a/src/hardware/ExaGeoStatHardware.cpp +++ b/src/hardware/ExaGeoStatHardware.cpp @@ -23,19 +23,22 @@ using namespace exageostat::common; using namespace exageostat::results; -ExaGeoStatHardware::ExaGeoStatHardware(const Computation &aComputation, const int &aCoreNumber, - const int &aGpuNumber) { - InitHardware(aComputation, aCoreNumber, aGpuNumber); +ExaGeoStatHardware::ExaGeoStatHardware(const Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber, + const int &aP, const int &aQ) { + InitHardware(aComputation, aCoreNumber, aGpuNumber, aP, aQ); } // Constructor for R -ExaGeoStatHardware::ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber) { - - InitHardware(GetInputComputation(aComputation), aCoreNumber, aGpuNumber); +ExaGeoStatHardware::ExaGeoStatHardware(const std::string &aComputation, const int &aCoreNumber, const int &aGpuNumber, + const int &aP, const int &aQ) { + InitHardware(GetInputComputation(aComputation), aCoreNumber, aGpuNumber, aP, aQ); } -void ExaGeoStatHardware::InitHardware(const Computation &aComputation, const int &aCoreNumber, - const int &aGpuNumber) { +void ExaGeoStatHardware::InitHardware(const Computation &aComputation, const int &aCoreNumber, const int &aGpuNumber, + const int &aP, const int &aQ) { + + SetPGrid(aP); + SetQGrid(aQ); int tag_width = 31, tag_sep = 26; // Init hardware using Chameleon @@ -107,5 +110,23 @@ void *ExaGeoStatHardware::GetContext(Computation aComputation) { return nullptr; } +int ExaGeoStatHardware::GetPGrid() { + return mPGrid; +} + +int ExaGeoStatHardware::GetQGrid() { + return mQGrid; +} + +void ExaGeoStatHardware::SetPGrid(int aP) { + mPGrid = aP; +} + +void ExaGeoStatHardware::SetQGrid(int aQ) { + mQGrid = aQ; +} + void *ExaGeoStatHardware::mpChameleonContext = nullptr; void *ExaGeoStatHardware::mpHicmaContext = nullptr; +int ExaGeoStatHardware::mPGrid = 1; +int ExaGeoStatHardware::mQGrid = 1; diff --git a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp index 8573008c..f2d98acb 100644 --- a/src/linear-algebra-solvers/LinearAlgebraMethods.cpp +++ b/src/linear-algebra-solvers/LinearAlgebraMethods.cpp @@ -46,8 +46,8 @@ void LinearAlgebraMethods::InitiateDescriptors(Configurations &aConfiguration // Get the problem size and other configuration parameters int full_problem_size = aConfigurations.GetProblemSize() * aP; int dts = aConfigurations.GetDenseTileSize(); - int p_grid = aConfigurations.GetPGrid(); - int q_grid = aConfigurations.GetQGrid(); + int p_grid = ExaGeoStatHardware::GetPGrid(); + int q_grid = ExaGeoStatHardware::GetQGrid(); bool is_OOC = aConfigurations.GetIsOOC(); // Create a Chameleon sequence, if not initialized before through the same descriptors @@ -131,8 +131,8 @@ void LinearAlgebraMethods::InitiateFisherDescriptors(Configurations &aConfigu int full_problem_size = aConfigurations.GetProblemSize(); int num_params = kernels::KernelsConfigurations::GetParametersNumberKernelMap()[aConfigurations.GetKernelName()]; int dts = aConfigurations.GetDenseTileSize(); - int p_grid = aConfigurations.GetPGrid(); - int q_grid = aConfigurations.GetQGrid(); + int p_grid = ExaGeoStatHardware::GetPGrid(); + int q_grid = ExaGeoStatHardware::GetQGrid(); bool is_OOC = aConfigurations.GetIsOOC(); // Create a Chameleon sequence, if not initialized before through the same descriptors @@ -194,8 +194,8 @@ void LinearAlgebraMethods::InitiatePredictionDescriptors(Configurations &aCon // Get the problem size and other configuration parameters int dts = aConfigurations.GetDenseTileSize(); - int p_grid = aConfigurations.GetPGrid(); - int q_grid = aConfigurations.GetQGrid(); + int p_grid = ExaGeoStatHardware::GetPGrid(); + int q_grid = ExaGeoStatHardware::GetQGrid(); bool is_OOC = aConfigurations.GetIsOOC(); int z_miss_number = aConfigurations.GetUnknownObservationsNb(); int n_z_obs = aConfigurations.CalculateZObsNumber(); @@ -267,8 +267,8 @@ void LinearAlgebraMethods::InitiateMLOEMMOMDescriptors(Configurations &aConfi int n_z_obs = aConfigurations.GetObservationNumber(); int dts = aConfigurations.GetDenseTileSize(); - int p_grid = aConfigurations.GetPGrid(); - int q_grid = aConfigurations.GetQGrid(); + int p_grid = ExaGeoStatHardware::GetPGrid(); + int q_grid = ExaGeoStatHardware::GetQGrid(); bool is_OOC = aConfigurations.GetIsOOC(); FloatPoint float_point; @@ -1314,8 +1314,8 @@ LinearAlgebraMethods::ExaGeoStatGetZObs(Configurations &aConfigurations, T *a if (!z_desc) { int full_problem_size = aConfigurations.GetProblemSize() * aP; int dts = aConfigurations.GetDenseTileSize(); - int p_grid = aConfigurations.GetPGrid(); - int q_grid = aConfigurations.GetQGrid(); + int p_grid = ExaGeoStatHardware::GetPGrid(); + int q_grid = ExaGeoStatHardware::GetQGrid(); bool is_OOC = aConfigurations.GetIsOOC(); diff --git a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp index d2b0252e..3a605b22 100644 --- a/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp +++ b/src/linear-algebra-solvers/concrete/tlr/HicmaImplementation.cpp @@ -42,8 +42,8 @@ void HicmaImplementation::SetModelingDescriptors(std::unique_ptr Date: Sun, 28 Apr 2024 23:38:21 +0300 Subject: [PATCH 59/82] finalized documentations --- USER_MANUAL.md | 19 +----- docs/ExaGeoStat-CPP-Manual.pdf | Bin 1075902 -> 1078489 bytes docs/ExaGeoStat-R-Interface-Manual.pdf | Bin 117727 -> 115185 bytes .../Rcpp-adapters/FunctionsAdapter.hpp | 29 ++------- man/fisher.Rd | 2 +- man/{get_locationsY.Rd => get_locations.Rd} | 21 +++---- man/get_locationsX.Rd | 50 --------------- man/get_locationsZ.Rd | 50 --------------- man/idw.Rd | 12 ++-- man/mloe_mmom.Rd | 14 ++--- man/predict_data.Rd | 12 ++-- src/Rcpp-adapters/FunctionsAdapter.cpp | 58 ++++++++---------- src/Rcpp-adapters/RcppModules.cpp | 6 +- tests/R-tests/TestDataGeneration.R | 30 ++------- tests/R-tests/TestExaGeoStatAPI.R | 3 +- 15 files changed, 70 insertions(+), 236 deletions(-) rename man/{get_locationsY.Rd => get_locations.Rd} (73%) delete mode 100644 man/get_locationsX.Rd delete mode 100644 man/get_locationsZ.Rd diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 85b610ed..1ebfec7f 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -308,35 +308,22 @@ ExaGeoStat support 2D and 3D spatial locations, and therefore we have getters fo ```c++ double *locations_x = exageostat_data->GetLocations()->GetLocationX(); ``` -##### *ExaGeoStat R Interface* -```R -locations_x <- get_locationsX(data=exageostat_data) -``` -The subsequent arguments are as follows: - -- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data. #### Y-coordinate Getter ```c++ double *locations_y = exageostat_data->GetLocations()->GetLocationY(); ``` -##### *ExaGeoStat R Interface* -```R -locations_y <- get_locationsY(data=exageostat_data) -``` -The subsequent arguments are as follows: - -- `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data. #### Z-coordinate Getter ```c++ double *locations_z = exageostat_data->GetLocations()->GetLocationZ(); ``` + ##### *ExaGeoStat R Interface* ```R -locations_x <- get_locationsZ(data=exageostat_data) +locations <- get_locations(data=exageostat_data) ``` -The subsequent arguments are as follows: +Returns all the locations values. The subsequent arguments are as follows: - `exagostat_data`: pointer to ExaGeoStatData object containing the spatial data. diff --git a/docs/ExaGeoStat-CPP-Manual.pdf b/docs/ExaGeoStat-CPP-Manual.pdf index 8c627d6c583e33372468e762151aac4b895a61fd..bb2a09ab647d91b587d9a7e3f08e94218f287972 100644 GIT binary patch delta 494925 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 `>mRh!pZ5KpBz&Bp(dtuiyd6b(H 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

    oh=P=Q6rjYuH#ttdPM!9H! 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 #rdhRxDNrJA#jZSBRGkWEF1nc4Ob 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Μ>s=)ez#V?^DI@QsMIh zvIH-8iv#1c(V{<F?Yq;e{C)P=0j;eHthfWc2-g@$RtB>T?vNOYYt++ZJD~dh760WOt5N#6Ukk%!yVX zEG#~rPsW_U>bw7b2M=E$YjNXW0pug0m@M~ER_K`Y+R)Mk6MFnT$K;5B4@%nDrj6)V zG-UiN#Fbmof3RSmbfy^7o|q^ZL8LZK^L|t%*oRu081NZGr^bFT)n{4lPiFsOGUagX z%D{JOn^lMoDSE8Y1*k^_Ex%{PB4b_O{R3sOP$-muK$@#B-FKgUq( zUWS=YaX!MgA=~zP>|hh{3QOUl&SlS>lEVGui$LefnTp?^uyl5MHu~w?Kci_NB z*&iidvHDV*F=d8+3RYw{*ftQ#69cgCl`MBVT7LKAQ)8kIu-OSa-H}9lQ!VcHR@2AM zCApzJ8kbIO^1LW9h2jw6XkZx^Q+1xk{B__ zWdB_GLZS_DpL``p$T?(M!a|>}m}s-b*#+l~H3m5&L3!a&25vT|?N-4>E*OCPET>-N z=S2kqJL}?b2#CY<*jFb0FO(n7ihIpVd3AnOwBQPTxUiNk&ic)>7TW7Tq#2~;UKqRI z=XvpqK*dtG7hq;0_$Io8rS+f#4RrDD_4XPpUR?qG%&ZmQ4bG=5W&}D)p};m0Tw4x9 zTPi;R-H>1dK6eI0M0ids{yZg6MyMWId-SwYW-4L;(P8}`aH!!^;6FMPhV^;SS56r- za&cG}bs~OZ1h9x2cvFn!0bB@>j*h=uY^4N#wQ)AkL|6^t(xa_Svw4q*Q5yGZuz{T0 zh4P;fGhq-S@s>1nc zSsWLA>^JD~*+}_Q5Mxjenv>9;X(ORYBk3bi0Ch--+vT%XBH__G&!ZfPf?5g1S!KK4 zv^>u1YyrJBT4a`RtVjOzk6Aa+mi}QT{oQp8NLZ;q5uT?#@CNi)On{B*^bW>2{;3Ys zhUzoEfHJIilcRVC#@`>K@{wHsR^eIyfj!&kn?wIYcK+{E5i`SoJPBO-mbrZ+-MpV` zW)%s60YVC$p~GysQ7O7xAca-$iJ*Q&%-QmqPmX8tK0QM63M3*bm)2m*AsmfTc-aqG zH=BW3MFCk6V;!%De=@1bg(X6J(|&Rw705yUq6!DKJBB$~hXX+I=8VrS;q6@;90D%s zc=7oj*(M4+xuhdVep~%<1uvV8SbLd;tqMuB41;B6=aWotzt zfa{;OVWCgg4{xRmC4{1IJeV=|Nb8JtyWs&*z#lZdb2J?_;Off(qub@oC(S2ii(o!0 zV^6xS2IFrF_oQrR!=h26U$IxTy?L=%JrhAPt-3)%{a68(lRaQXdjS@o=jb`kuSoq6 ztgy|gZzYkEWQR{rHr+JSX6tcn0pB|#>W4ka_z{=yR6*YE#Ua^vZwBxUX{)s- z5{ejO@-7Nd>LZdCL|b*}Ky1Ylxd-jj4@y5OkWxWks?BU3G~hAmPw^uRAYVfC z0w70W90(v>J5J>n(*SWq0>D=eEj)p6q|pO#o1A;%8#qrs zYRET73cOHU$0mOb8VkI%DW!c}U+!f0p)l$1ugxEt-L8v^7OOK`!Zm%8JsGav-c0Az ziguJ9L#jKoK7YKHkQn_by@nf20?Y71lwflxB5f(!MGBH~E2hco|Jo;%O(?4?6Zl2z zTa9nZc|}yrt9q{$ZK#>7QK{z_qgy<3c<1s9jF_NM;HiD`3CyTHv#Xw+SJ;^?#CxR# zmz3M6U+k|~Xq%nCSp#g6T@Z>F>c%fCUWkjL8Gdc||KvSgoNujzqj>1=6k25q{Q`JB64v5jRT#er4Z?b0beI_V zSYyj(%wF}RUGrsI9mGq@3k;3)~MC#sd&hmbYgv z+Nw7xl`TxZLD@$1XD)_Ng9F8o$Fkg6!q^1Sz0z}+kcSM|2f2zM;3kpNxZUxgbviCZ z_ytE6%#IrmR8at29a04pN;RoYffT2G?_I|*rdXH73tb<$8DX%|!wEWFRJ^o> z9&kV6F|Dj2>Jw!JVFMaViQ;g$nYs%zfj9}zd9Ca?WS8DY(7(T@MOz>S{-xS7 zvi@)V*uT^9IGO%mHD+e`mLmV(-~U%{^^cd*W-u-yPI~UB{cqzPcmcn`=!E<; zZA6H+O)vreIBHE*O=yxfvA>_Z4Jf#j&gNW18Utm#TJUWR4G;H|G!~6;f=~rkC|sb3 zhJj6CP}G(rWMZ+D#4>Ue#5x?>qW-qozbesmlHw>j3w|JqzYG$qgz--v@DDP@3peuY zN6n6NbejYXGfTzI7V;{}m?E_CyX6%aGioDpi3B`%*b`fMtM8NZsAV2w(P&dfL7Sk?0FPx-Q}qf@ zr&RS zM3fLWF{Du7lwc(WL~RF32bcF5Li*}!RcVr%iID253A_r`hQ)+O-k9Yu(54Msy0yvKk-Yogh;amjS3`o^%u1xR^y9K@1naFeHrvV# zJXo|6Qlog3>dgiPvKu(;_Z^HtEM@%UGm_^`n?b(_I6t(nr(2;ndDww3FNrY9NV!4P z_kEM=D)p|0-?^~2lk1wE(&!Aakc62DS(t!4VdFk;oZ$?{VB{zS>MVmXdcq&$T#RXqwA@XBNl-;SS>&8Wf^J*d{KMx<_H&tr7(JyrxVRDL(qElu_|BhasFm-z z{wq}vo?ZJ&D#a!(>a=tz(mGCORgs_R8|t5qcj_pzLgn$d9>N-MDgF>$=Nz zd-T)o{U4-B4QiwbHjpdG-Qb$8w{1z4w4II7oxE1 z0P7RX=h#Nr-qA5gSqC-R`p@U881{2>)`Kx~jt9nKK}04X#X#W#KbGr)N-ri7P`3#J zn}jY(FsMxLzxC|o3cDB|bwE)-Gf26mAFO;^6xW^IqZP7F2|Z}9`}&N3)nxq&dB21j zZPM9{DiWv5$F3F;huU%FSAgp~==+I5M|&e)k@x=6b%>GtFb@Ti>qCMtbRw7tmG@vS z+=x3c;CcUZhK1+<4nHEaF{$b*CTV1f(Xe96X4oe%2@mrl9B){PZ1fOf+_EW-3if~% zr#=gFR7%1=9j6!;L#*6H z6ptE_K*;6KpB8v!OEsQYdb_REfwq7zPkB5R#CDSV^jNN*fp)YX(z|&v0J(v5(R8d+5%gzQQz z(OQgPz+#Th;o2D{T|h|6(KX&r54q3I>KT)%gjY%_hz_UvX>^-5g^-~s73xu)rx^B| zT=y0?I5(|Z_=cmOrSKI6=WdVnTxHuzHWlsW<;Hg1sj5~<6$|Hy(hT$!51ksy59ac; z70Sv;8co&SHGDrWlO1`uXNsJZ82-@C@||7G@)dXGd7bKc{MV(#o9EI0NBB3QqYVz+ zx<`nmk-QO;;VvFQ$^t-+9Aw>-dxP&iQn;Bzp!q11jfh4#9YU-pJ_MK>;$q{lEu<(| zS%FYqJ`~I=PBGr@It%T7MPL{%nK1EnHi|z9%1rx=Pfn`fkH-_(UYG!goy0GSeT@*< zMG5(K!{-)QTy3E+%Zzi;6~YoTWHwSk0>%lJL=!P&!vdC~?BMINN|NmX83N#C zqAyJhx^EyauM4P^AOPjyD||-1oxCi#c-5RV@~)l^@PQ(k}O$MZZSb8pELxhd+4Nn~&VjZv7(H8~f!(34&X>w`KM# z1OIYBASv$+CRQZ*qa-%cA$Jmb2y5L2Cv_;5EVriUdm`)V*k zJzL-z)CTe*ay9u!=_)v19}?J=5ogPyfVn0A?vqD;5Add3d@w91$msqim`xZ%xGqf& z%O$qNh>a_l5rE)Id4Sq965-bmpHI6aSP93v zcpf8g7`%e27Y4jw{!ZgBn=I4p+l$pt?JDpp%QCvp2Id=yiew=@elw1MjJB)D)V!oN zALC0{qEB>cXEJf=JwA;(5i4I3fl!z_YHu2!nLS_C;2fkIs%G_19Ya31jURLyu^`Rq zplZWP>Kjs0xC)X~)RTt~FTwpIo8oQTa*gD!XS^&Zp(9ZJ!t_<-U*-JsTkx&cBSFPB zUvPav+>4l*C_Bm%`rxr7Z6B0RwzKHpN}>*|HvcFt%4V*^`!_pKof$DC)H}@jXoYPtZd0 z16C~=B4!y83%{oFr%v3w4ln^ev8+7)mdra?0@k|6KQu&y(g2K~(k8;}jL5dGg?>z$ zBcpV%2BJ#9GLHkPeglSY?tbhc86DY=mw_c&=XV_jQk>f_RKs$cKZh+fWq0dVQc#SbGWDVK^uAKUC#zg=aQgAlcH1sDI zYa(=F2e&IHA=p1Fap}vHr{zqQG+V6L+N`|{^kLbk*}ux_1)I)aFSK35po>hUmTx-X z16#w0(TkhHsM^hR2U4+R# zI_6_TS1q*JMOQxQ%ne4aj)oA*&wp}_f{&$rMPIby;zaASX2*;mH%4iU5Qe<(zTg~n zi;d;PnVVD(HeP0)ZHt!?$n4WM)jcTjaqhzBJ@*zJ>#!ilwiAe(k;jVU#S`So7QtLj z<|s8^3K}F!;~C*Xo9{whBN1NQ`?W=Yzsy}<>@9>z8#3u%vII9jEo)eLZXxzt%SlAF z?ST4uy>>@roQf$UtviO_>94!svNa(WMub^4WHcN88C=ejUR#`F8cb~eHn`_x`j0_nTj{?OGT6R}KA)7X=c~*nB1rK- zvL(!8+Vl&>Y2u2{XZ8t%YA3TVy+^X~?@!u(wSA))0SRY^gp4K;TTAC7*Ag(z%V^Cy zGCDNmGuCY;ZKKHj&B+MK0ddrUN3JMP`n0OcTZ;}R&l5Yenuv#7Y@4c{9$j|}j%+2- z++AS-AM&DU-}~3XVHt02nXfEFjBwg|wymrY9ph2yN)|%^=QY=busKj+YMBJ z5J&r#Tu)Yh!-AY-2VfvCXCfZj$-{r(gjDpe6{RFJIX`ctAYW9=k@dV;V3yRvUi#*GlEGh9Pb zDgTqKQRCmmsfXf5H;1q_H6J?1`$^#{nKSe_zZMJl7=dK7><< zl_yga1r1y|cJt#(r@D7ynvKep;BC8@R4Wvr=r2D{+6EHX$Z;v)+{NWIV7@Vucx>HW zWxF}dTSn2y`U!}SInYb1pS2-tkQ=Oe%K9PD`oAeBDUoAq+uD8K3UDecetVToEOMT0pk+CJtb1LLiAwaJC> ze-SlnW4#%Rt}V*udXM(%3Kma=tSXt<_2nVvZ#kjaLnEXZEgOlHhDT~<8XU@XNP(5~dW}4|TYTVQ3G3sMIz<8V8^{mTH77CHwLdj`~&v>uVG9 zTaA^74)FyB4f=ezz$WOut>XB+l3Q0qa;=Bvo7c08@bhS)hZl74!L0f`-LQi z$Pc|{-k%u&%;Y`HCk;XoU3xdyE&U+9^|66ufeso2X5^Vza$`9eM%#gMVJq)l!Av1C z2flIEmDGp)1c*-Fs4%_=#x5f!PUDpHD^fFkj zRtj$_>AxcJ;jKZm9<6>)&@Xxu?pRY>p?+n!f(LX@LRy9)C6Tp5{@lQ~;plB`HR^6$(8>A6TW~KsA#j4=^ zxQ&6WH&(avH!Q_l43jADy~=;Qq-+uY0bs)vzo5WOSD{&?((+fKU<%p@S=OVPYFtiQ zX0sbH!!6#Fve0&;*HDCF?K|flaDR&2$n}FXu{6acEeK3!9~|QQJS(Qj=58;Y)9er zg5L6@nyUQ1A%ao#0GCt;=5qU!PA!!nd&dovSM{wM_y)C4!i3^xmX${a=7>NwvvmNg zOz^SHY8PJK0yYhqmasbhb#NJrgR1H}M?Up|xNT;So=WO#g{IwhvU!p8vR|#S>yJ$Y zcmC{gW%N`N*>X74(A+H|nE+0SVDN;t(Yv>MjHs06s&Xn4paMG zst*)S%N2X8#)A|0)`8dwwE|pOCvHADP|NKa|B=Uc^F5D02d{s7@N1-%yqr^>=g z>t+Ch^6)aDBCn=wJKpGG86{|+)mQgiu!IwEEQIipXxvaX z*O$vKcAa?*c|^1y8FvhCKnNnvJ$Kx=eguAh(SnK=-x7_xoUZAu%=g0NF7R$Wk4=036 zYSP~mLd32MRm9UE(EKTHdR0@HLT3XOemQn!*?K(&5?bUUv7|!%_Z^dDE$?s@s{!6W zx|;DWJJa2655^RimbjN%G~Vj;+de4kcX7$_oEMtE^bRG@D5T>^rn&6ds$(dgl9gVq zF9Caepm=@P%bNW+9P|~r8dc#!Dx@8*OH7NfMQjuuj9kh> zj!25AXzL00Wze9H@}n=*e!W_4Mwg-x)eQaP14X!Hp9XHczO8>f0rAN?2V8m6^PaSu z6+JwH17?r7AG30H-IvZ=oBm531LHC_8f)hl-;qqKre$>ckVq?hR)lZz8?qLz+b2mA#CuKZ5XFKpiayK>y zpGz84f%H&9A|f>AKNQXj85EmV8C_%`xhTjTDS$(mju!K^a#bo`wK0Ay{q%9bx6m!+ z%KzLB-=q&-EE#do9bC@$oFRk68MJi~+Kw)Z)bFB+Fj?6SvH~0pRQ(cxU9YA`B%xd1 z;^E~;f6wx~=}xGYH~Dt$B%)#Nn#?UhuFQ9W+5P+;kto4E{{RpwDtNEBf_vAZ5yWN| zD_XW06~+Tqv%|{1a~W}nO>*Ae2mJZyuBPzY_ZYHsH+MU^87Oe5mV38C1^pK47vxGT zw3;a3k*#9pF^e|Kw6OoJKojBCzGCH^uFeSk3Dwm`ilo-2*$#X^_&%VOI705Q!`Xue zRC;04vP-t!>fsJ`m?cWJfQD0;=!iO&L4@B4_Xf2{ztL0$QBZ6pwxdxgx8u{B2uQIV zrF=XQ)pi|k2sOX3M%${Q@}Zw>-47~?_Jl*T7z9ko-;&`cjva@-TdZh@ZR!NYctzJp zU<6kN#wtSQAZ5S*Ws z|2M-CKyeP9e@z#TkkM9-O_=~|Rt?U^(%oWas?qf{7zf|Tk0FSWwdzvU^egv{A6-r0 ziIW{{^1ipY3-wnbe)sYxC13P6ka*sx2J(?Z1jz4f3jxV)#=ObMZ3Vb#X65UJV-fYGJ-`elu9 zC_kiQ`5c_D`r>t!GqBhCG_fKxQoClFWG8;nC&(u1&O(47=y&8FI%de&=XVxB{nu5@ z8|k==a?MmU08-pJNCa;jt4k%$A1NH!?^h(ph9O){e7M*l|aKawJS6xMVovvum z*0_N&?6UbK_(oEYJw`)4oZ8nO(-r>REfW~=Wnb8n(A0m$@#tgk2 z;wu-~mA>uCVEk=XE!cr90!b|d2>Re|ntRAl)-?~A;pD)+v{uv>)^lKi<-OqxO6*UBC`m9dR=?sI8Q^& z69rT$;K1R>lC4q%H5#eOv)~kD1R~9MVS5W`$y>671nk`%Stc57;VqE0X%uI)X&x4M zwrjbrO_%7M+`xm$`l=J|cx-BSx zx-43NhrH{ox0}qk#^G=6u24==feBv0q%JceKmDUNTbSevlDt!is{=nK<+WTg;XUMZ zs@j;|MUteBQQavqRQ?T_fH7IspAaT*A>7(7l?d%bW-9@FOk$v;c zTQs4s`dNDx8a9ZgPzxJfiUGUB>ySIrXq*5=BVZrRg_a*qUd4lz*PNCNgijz3F{oWO zrf<4+r(P%Eggu!Z7}sX|lDk z-*qcczW6j=2qf$|RB#vyJ;8}cc1_97M*-q@-g(*)BBEhCG~tAwJFzQdUp3n^DIPts zl_na3VFKeMjSU$x&IWVm$&o|ty1w{5AOyK`%hxNvv5V!O%3(nYG4w!sBfDkZXfo@wwZ)qn_6Scd zmrp)lIpErXm0)Zob4voVUm!m;wbm5!Y0)BzkfP)?YRh^ro)P9bI8WK+Hi^$1)@XZz z0;#h$Bew=jnI~>E(sg36!Z?}hhz>~F?eijDgz5}Hjgt?-@g(ARosTJJ4NL>vDvO9= z_^X!VIL+pY>(EeZYjUk*)uhUz4wL>SJpW2~_5_0E$jzU8eDG!A|H=D+nWtvDkSgLa zh~GinS@Y;TpPB6ULc_M%ymaLhrM29o67`WHg0>h>g6|CHn)UgZb9O|(_w`(0QOf?* zea?VlVV+Y3|DfeGdQvWARY816gD+U#uDSkp0cD9XdyNu_RnQtx~j@t3O-m*P)R}igyFyb9SmTpwNz#f=p{2<+B%LrsyxilW9vzWzuk23!Ok^w%5Y z?U|;XXm`y%0iSGdBeFTZ*4nuSy;Pt;O4%>bhHk^{S->#`4uG;YAnlldWzLk)uCg~hEU zRm<~`Dl`tsA1p$dfH6D>vM^sPR<{n;x|lJaBvT$!)=BK;4?D&zlrhTRk-M#Js1K>z3!Xur3Lfur(hAWo*mY316Y4AbgSBsZZ_7ux4Kv-x3S&?V zLpU%HB?_?8zMbM`w1hR@@dBBFo2>Y)iWJokB;Ky#*i?9BFRRD-4B-sKu;_ooZ!o1I)o>;;1Q=bQ7poSoq{`fx zF)xRYfB@Mvey%j@P>%~TCu3;(r2I`oq5SZtLzcb>vb$2^C?X)9!m;McEV|S4qL*C{ zONCHgZO$02Uo248m$09yl^dWG&u&>(ti6BlXNH#E^~>1XpNLpzW09gm^-CE(Xe|Fh1D-Tj=F`UrQXmO3fn>7!x@=-jI-}2 zbrCnM_3J%aepflFcVWUnCxr5^aaz2`RiX9^<2gI)xoK8EYi6x1g@QxhM^^->0kM(z z#x7)lW4G8~rEFb}U*h$8Zj9c>BXo`eSdHbxD9+1ILq=kOjpx0pv`Ncv{yYFNBLhk5 zfdcj1`yTQY!y7#9A1iT zMwP`1%oGH{m)l{X(%x9jDh=<&h@j4#>kS1p5w(Xt+K3?g{UI_LQ)gtueAfU(-{6zL zs>#~dyoW&`6XSH1w_;Rk>$p;jdHe5PNIezC^32XK|DT&2^l7DTfn&wfarv8mLzal2tM!LlwO4u%?KFG7+wvlsfbWNjx0; zjQVvSq&yfh(DBrBjCe2i87(SqAQY?**V^bp0I|o367sf9G~=S^EMf44}+U9H!{) zSTpBFEOzwOt~p<$g43}5@fY#eC>7VFjJ{@Y5AU_~20GAJg6(CDKYzhHP-mRcHe6l6 z4*Jmv0s*9gX<=dX*V+IyK#qZdId3oaq?*x7+jYn_96*x1PRO-z{OA>uSIuahz_YPtAn|Tid*{aj{}Pvj)6;g$Yaa*dB`%o5 zMTPlUL5Ar>c%L`tZ|6E-lo}teqb8dZ%>lZn{MpdeiX19Iq53Hk7HDht&6t0U9Za_Q z*+e_h`f)dQ#LuU6_nQ-bsq5eB{_VMTQ9V6c zgHvU~wqsPh|-pQ*as&?aEj7PvbX%649DGRELK=?4{T-p)|#&CAGs zh4INkl=(*CAU5jGEQ7U($BeAoan=JpPfG zKIlqWe%ejeUey#-78Uf9Yxjl(k;Cx?Oc*2!X_08v^t#Az5bgX0!$?ErSZ3~(-<%5C z%@LfygR%ehq@i0t_mv694~b6)f&6kA4?PiJWqPd{1XArS1_Iogze}+n z!ay4flebRv_E*#bGiByesfFMVePQ4_y_0hup%?-^Ef`z_GqN)-kMF7_U?k%3vJ%Xg ziwtE0L9)x{g8WOstn~q+JwTKOYFXVgqqQlVjqmKp)OAJv!xLEX@VkHG9Es0y`T?z1G{QrI!}mHS1*)H#aB(<&t-Dmt z&B$#IE&?eD_A34ALr7iG81=0NwzP^sW*Lh5Ly02;Z|92Qbd|s!;s!|u`5>e|wt7YN zVVRC{dCMEUt=57+Ps71o0dLT6xQ*ULrsaX`a}nBYPqcJvZ?Ec{fqXvVcX?{Y`Z`sJ z)A#mS3jcEVOrsg}e7wxHP?D3ijN2-MR>5E6c&mCD5Rz=fZSWYtAB3^f*zo&F%0m~y ziM_y_n_(O(*Ps{$Wo*l@9ytGQ{`I2PJAGiCW$}e*61f*bH#P{tqYEBBc^9$YC%Pch zbL1#FH)B5+XIVOuPMb1eP*od5iTml{E&Y$=uV`Ej0E%$<_F3rIY8lO)EN;lF+ zRIlf4m58LFzC@9Xk;rNIqRMVFguYMb4sgQM>>5)sdq_OZM@ZnV=Y*mX^u}B|qQ!id z#?S$9sT&e2!#Yt0@36(Koq_^Wav65b-FXpl`#QwW8rWP4gh$QrmL|o=NjW9DCKP^y zU*|DJy!hO=H$+n(mzOlR%sz0FB}SMwnNuykh|{}G>W*p0glATK(O1miI^}8qhq8AH z5^U?Xb`%jUegH5uv413);7#glJRRw1=#x-=RLoe< zk@lr2jwdcHo*~w%CIDlN(FmWHo*oS-S(^2w>c-4|XexZ$e2$8948-6#LNNaGvXhLjMLe?xOrh103uB9Jl{p z(oZWFNGaa3;Zs3tJ7w=&yy^)D>Q_=rTMdWUU$&or*aa zP0SVtQqKZO%t0xnhTBuJ){It*kfD}-L$_{=idV!luBH?I$!d6xf`zB1kc`}*updxb z{v<`94u|TA!c8@L0L%n#qvQ}76{UcF)+*{kJ@^#FUnyG7NgGAkC(f=gDo(7}Nlwnf zlbB^^HqA(m&Qp)r#6u|~WTly^U6|@qo5IY3u8Z4L%3fB0#^P_Gq4(Qd!>+fwD6KeF zQs7dm)`K%0x}4a&L?J>FbFF2u$cpp;L909#9EF4AJ^8oOS|nryJL*)ks7reEbhsi5 z4u_JQ#WZ=ZWO_7e&xnaUX=RFK0>G3~Aq8G(l6xI_3m`T2Nn`h zvf9>v{xWoElv|@k6!7;khqR(hU=V8dg4IFj1yX~ukeNc~$XT#^KAc$HfAn0Fw!HeZ ztm93wPH#PjJiQJtcj!*D#yqF&JG~O(zYL5|>azK+bGr*%4y+y>o3pq6g3!{Pj4FHc zi|qq^F}lKAB!cOE?(%#XvCeLrgnvKwY~gVA#oTgIRhr(Af`Cp-S{WDI`g?%N z;flzlkPZ*AY&=nj@Pk7YNqrN99^@wt@@bFdAekOl6W>A7-)%=NQS&X?9Q1KDlW_zL zjB@&z1a$x>;KJdYSlcjqeXn>r$ zV3nPw+c7Gu4HQw1xwW`GW;_-ywTSGRv}4Ms}f^JWJES6bt1hBo7q>TTMj!N!(C-V zHUP!I6b_ySkRi66CWI%s%}w@a@0~&Ns<1LJcjuRUia%Fi7`~e6+V0}jS`EQ_{_X14 zwXOXLXkc-xg6V~VoJKC}Zf8LHm|3l^h;jP4tELzLF9w7eP-LP+41iHzK6zY=Zsn6j z>pGu7y1$LpR*Zv4y&<^8j}RhLdc_=iEU242O=u)cpa*BECl}&L1x7QAx9)eQd^VO^ z`HGh6mGw$QiiJ7Gcm$#hMW>Y}e{3HHoyOro*&fLeykogbQriR)$ogw>Eb4}$m87DQ z7qp$LKqXFAzT&TEfl4+5_50R=Gbn=K~ zB|KNC41?;S1pA&BZHhAsZx_`Irr4}?Z@dUgsyXa6><=5X(6Ls(%deJ2=}Sv zjQ+{easW*6MCF;aSpXO|gR||Aj~*osTRb?{VQKCcoVTunZivENxJPUgzZvIj0GJFr zJZL3q$QG@9R|{^Yxs)^dxk`(oZTpQ}^MdTa%I}``eB97#s7OHGLwp+VUx|nq#sj}P zKOn|QugLsVU(ozqvtBd&o;ghFd^l;1hU}FWjVCCCb$?033GIxyV>FPX>5zjABw6(~ zhGNj~$04CH7C0!YyAcNx6E|H1$^n^-|H57ch1LgsB*kvkzq@G8S^}boEdO51?<) z*Yb$^Y{n85uE%NK0H$p>AACrxU)FQn+8Mp0_E<4$&~wtS{h~oy;0bYGDv525r&tB` z%O$USWycIdx`NkoFDH6tu~tm})AIYWIf>|eK@~QxxXH96zJgDuG52=#pir&9$CjY( z=my0ZD83_cN3m{~0tx%#YvzDk137MH#<-c3OkflfFPf!ck{;?n=rOc@7Sj)VJ|N+q zU597~dA-YM&*ceBKm&IwEr%3C2-1M%w0={n|2N4jjU;JDnWh%wrry`m#+Ty+6tH4Z z&;(*rCH1~=)urM>&;*5BM~CKL>e3n@q%TGjg~s2|Q9nyfW3ASD*vJcidkiL`$~{3) zid@n{lE=lSvC|>?Z;?}{ki0Ql!>TsXu8?KzmawU=jqqE=amMwW~I*7-z^qqxa7jQOwiae ze*PYrQH3Ff)$U7j_N@-qfykA?6RzmHjp!!r!b9Ct+@@C829TYR>J|B4ljErgq;vww z4A3{_OF@iG*wHC>`9j6*a!}=cI@D?-S|e*In$P*m?-Q2d4{&97COc zn<|~U_`#pnMw-|~E_2zWer`VVP*X6OY`$mCWpuep#Y z)Dw-#T>R0MH$CEg#6y6Pq(ZJ$roy1luAI5_#nhy~w2!c1^@NC*13cfk$E8ed zx7o)mMsKX4Dz*xzu`uQZs;Y?cNsIVKQ*wdKva%294S=N6inct1OzyPUhU(eCzE$Y7 z2?;t-gNB@gzIi{ahX(oqQ>tsL;7PwM^1q-Cip-dN_d6jlC48ha#vPn{ol3OGP)M_~ zEDep~n~c&cUZ9&;*AW&6ebkYXlS&Xy64)o=G67bA7RQ4jJBK!&1dIJP!I%R=A9D{O zyzQ>QR`;$J72BLDNJ<3=95Ix>;wK*yyZc;?nvaBz+d%ABbwBmhs`@;(YVsD-U+upn zG7dDPdBu=>RsTVruk>qTuMr zbM`v&pRHh`{7Prym-=%&UsoA%OHn>}kVOy;UpvRmaMJ1G(Maf^LYR*AR)p-iAvbUB zu7pv@pMrJHY-N%8qjH({xzQ?5cb$on`@64(?@N~>-22P?&SyPBd(%h=>Rbp@DSl;m zmS-dnATsgC*pj>wD)w0MkjzKI7Y8$N*n*O;(chZlH}rL=_>9bitk9Ek&gpr6iH*0N zM42#Zz|zp`mF^9cjQR{ktY*KzYNC99WBpR?{R2#zZq#n*8d0F7VWJlJ@JPsW0}+O)=+WT|=u^rCxBp?ep#e#tsE zs>etrbiIU$3|5g9Kee3hC2eU@(nMZ$Dg%0hABzpAvsgAVOp47~tgOgBaX!}X)TC7% zezipw+et9!eFtIPC>{!oT(x$;GIpF7zR8V5a!Z}tqXgs0HZvpvPllo>{#qT0Q_3Ks zKcuc?h%c@4_YhCt&l4a#*w95wSV7!t@nllFD-YUfY=?hbuy!i7x-=d=(9sDWKa#^KYzd9?(Ak|VEg=X2y@*AQ$E8HoEOz%&H ztHY%e7FjoK0e!zc&e(GlRm-`-JXaa>a3ncwr2b2`d_WjN7jb7%8TKiJoDf7wNhhI| zPCozeyj}11B^*?bLbDpQ5=N(UH8qFKRH<-E+zV|;=NoIzB zitV9i{Qo1kh*|ru*q(_3cRiP>4RGb8wF{Ckr84c4dD3u*2_#89u3AxGZhYlvU!czTOx)?jjAyFObh&d4EIpK(XZiK zPeNunOOWnXb)SaR19#uvsHp1sQ;*XG1BJilNLHR~`&-MnxfDdTgN?}=w4+X|o-ZRK zFutC$=%`cizO2cTgh{y)!8B+_3-1-fl-ZFEsJH{7lNv3xBEcGK@!OO03pGG_UIV4U z7RJTw#%s<@1({_ub$HN#FXec&$(d`w&FWIQrx%ndqZsiA8vK2CUd7 zpMa%iStz%2zIW=$Cd2wbBxQPU3b^RaIPf!(66?fBe95Z{Ublw>%#(~cHFHL2WcLo{ zh`v*Az4?$xXw)9Gs{54F@r~qoUW~6B$J81x>TNfFD4w2Aj^@{0ZNFY%djNQNbL{*z zJo|2=1ELV>u>|b|I7XraIq>!eEfxtJs>eJduI0QCiKfsauDlrTbeDP4-gXLiGIr7{ z)_ebO4&VbYFe0dJmP0A;rc?|T-V_ztdnN9{(liA0?hb;5vnB10&HCSp{m5%kL^_rapUfm&O3+L)6p;<@U zE19nzrtF;19PirE%@wu$?rf1&Ucgf3+#zJgyAjA|R`rNs2MTK$85y|KJAD9;QMysn@UF`VN)n9nQ&m7{zxr$`KMVJw%2$!!s(u^3)z zVeF(*Rhm<+qA}*jZN=*8JmeUxKs@Oid+U*MTd))f-!KNySiIEdY7RD58Ss0wwcVNS zY`~~#^C@+oRKyE>S;rk8)>uj0Wi4Ig=_J>vFt-k5;?82DkQoSpbk>|;1h6 z`nC$1s=bq6V9tx$g>0%5ryq4Oy*qt*d8U7J?0MhE?)MBLdn6fS{4M;Ne_!WND8ReyR4mqTCf3O?_&O4zE>0^t#EP$EL z<+|H-C7&8N3uJ?MM#Vs)W>)bZj&lvNH8@uxM6`mK{1r88LQGCwL*RFpJ-tDa;wo$(C=IRL6uoJf($or!0J z*fu6lZ2RuXMjRS&+yUp{kdwV)GUQl>Z0+qkdy-eW*?VU|EJBSHifR-#o zyh!makJV_^5fKipQX>z!r?vvuMM7g_>gYl|Fm%-@W$jb3x;O8Th9+w-^+y<_!pHu@ zT1}?}&j2fPYH$bu8vg7=nS>H$Oa)pkTP;D|sp9cpag)h^L-CPa@Wn-Sbe(SW%_IZ( ziJgGvD|1?C59jd#XEB^5Su8jQQaq&z6NPW3kPk;oGS~ESA*> zk=~$PE!JA9d*|IyPj}g*2Cc0YGXnyHLhr!Ymx+O-#6;Gm5I_~v>(R>J)<-yMWHB0W zM~*xIFhCi~r4Fw8ol=AzUlmL`H4Z3*Z=NH`1KqRrv?z;(LawygF_m+#m^MN7|>hw#Zk;rwXDohL$a zEq4XhfA4A{T^~dSnAYU2i)soy)9CCwU2;$pQ4rOucYCb2yFAU4MlrL^&+TvuN6$tn zC&=&0RJYq7(L*Ynu?t%`FY$Q57DNM4_`!=nJwF>OQ!;469t94$M$u2f?8QEa9*L$;@AZjLs9mQ6z*H-K7>hfY3YaUaH3dce;Yk>Zr$HFQ$E*Y{&H}sK**k2)qm%L7uUqQ7}Ct{vRba5;4-Z^1c zR8Kh8KLoFPgq1iL~ zz9|6h+sZP(1144L2(tkbT^&I;@`%{a+38K7cU+s9+@Tle3Q(3;TuYzzwh*vS?Zz1_ z_j%4cE`F#9s^0oy8P%&@e;n##84?;o`y)X3fbtrpka{~@%OW*5rJ~Htl{O;U8dR;1 zyK-9YAxB;KmPocm*0#F8mGZecOsrMeT)}PuSkYoBP1*bRq>*JzBp7W$dgYn(7$SHs zAD9N+y6gqf3^t*$Kp7RbnoiRWv)E#|tX$)2`~moO^GGtg@orNc>0J0`omE$Tl7&8n zjy93@+%1pJw;5r1{hgF1RUlY-KNQP&00BIxQt1Mu_Z0zS?Zcy|O>sa9K#67;v8Azr;mo9gn<-S#FZc;;eA#cVK zWMR<(cCl4aRk_wo^CVBLYL=+=kjMLqSAZCw#j~hB%b3uUL4eP2M5Z1U>Erw&k&D3q z$fYfd5^+Gw@-#4u)I5}uasvPq)TFVzCar1){Tx;`gWKa%>#GNlaHqHI^TxY5tm034 znFo&fK~1Z;5YH%84YS>!!ing6-4OXy5cAf`gZt~F`-&A~x3>o#&jET)?zk^-Y-)1% ze>%~a{%MfF#`N!ZR_Q;G8CHbh?RUkKMLl4aVN;1A`dsPyW`TK8TidmKIV%!GJ;^lc zn$ebR`F!lr{6jJ3+OGIZv1dM9U+yjWzOAZ|uH^#N``H@b(B6A_e3JVkw(S0qlsQE} z;drKkoOt$Jd1Goyll5 zO(7etpZ0>U>=XUYiK454vA4xHm5k<(2nQHiTs%VIBUu68X<;Ur7OW8c3U1*7cD38a zz*k2I)4i$W{!rzBAW0H9N;w1)JcAetQb`O^G!2sU0#ZL!IehsjPA>=qHa&m0;~7o) zM&{*9PB~OtdJ#T$PN1S<>N+3k3VWw-pI&Eh)cSRt5p0DG^KsA8!_cNOTP@^fA~Dqd z!*4Ac8fq&RFuFwm5V?F^2%Nm6R$s3G=++Mh<0p`^ln)3C7J=O{vq?YPWyvCacO7mn z*nCaA!u*PVd=8TSzTpFjY}~B>=4BVaFyr#^!9DZQrDGBzH5brze_}LvH&QN8*$!$d zlpl#27u-6v5T~)-%xzU*jb5J1+<6wqBEqRjo2-Pis%70;B9xW^h;1&@DzR|1H{=Hl z5*tiWXy=gG_$S_F?ksIsLZhRF zBiqWb!r1Cb;#BcLmSXow3j_f!@3+DtHqNW0!0@@uunNt=X==~0bkWQ`enP#?;eHBk z$2G?NspySf;aH_#frPZl_UD3>DLb}f6Ptz8(PsKs0Nlct=5lwI9%g0(>Fg#lS0n;~ z(n8Kofc=d-5Nfc>gL0w>!q-0F5Vq2wu=P{xJe_B6YQp;W?NI6Xyx7ls?NYn52HL+W zn6-uT`lupooWSMtQcDgWkF`N|l=-#>!N!am)zL4m?iW7X?XZL8uRZlMqcKN8_DC*dP-uMuT_r>Qvgo`v@&}~%P)Uh%)U!$Llv2b(%y8>2* zeo5(3Zzl8mV)0DN2FW5M-Ws3qioUW+riT#VLAbRB>;py7|eNP95+hfv=4`bbV zt|{I#2cROS8eHw^BW|7#9PFDm1L3Cd@;K~B)Uji$ks%vfAg+XT_GZEHB?%@f%ATCb z*D@kxcyVN4@F#HVi1OuAQ^{{@-uM%N*}*s!w(aN-?0#jF-AY?hjn^=tW-Ii-?ZP)ht`n0{=9$l_3O1=nEcOXKe`v)%hdR?A^ zA-ArB6vQ4uUh^0CL+;-v+=XN5{d* z*Ktswke813;?t_c(YcvZ#ppGJ5Nm9e`LAWcI0csL9DbP8k?4QNF2cU<^GK~>n&|i| zIz|WpNu*0J4#S)BvXgOOSM)8(yj>G}IL>AeiGbI2!y)$lA!eT10Mm%&1$cl}a$!(a{` z`}3Da+ugG@K z)cmFk8?#_fN6s9Cm67r!BbvUDHiN2OtZHuY_36v!ajQSWbkxr7nZ5&3KLeGE$?=K^ z*I#&B*A{1cZ=}KAtMz1#LwQbypCKy-QkpwiXsLnP^5p}moN4r#l%HVxD4Bv9@_ml% zIE7}%jn!?_r?eq!h&Ts9IJ51)WJ6GeFK@J@I>gS;&9RV+bm0-%7T(rRR>R~+-UKB| zWG0mUJey}8yjQ(aJeO(%4|{zMO6vZCgK1D?wgXAG>knqN1IDRi zwS$%YB7@cXzHKVOl0vvx>V>V97uc`?%kwqtiYBelK`561#rbiL=)Hk5+i3Mvrzi)d z39Zf(s3G1BpYdIVt3JiFw95v*uIL(n10nxjVg9Fsk@=qz0vH(>{&lOTCh52)hR}Ua z<+O$bK6LLRr|Bt%-H|!4G<0;-+Kdt#R?bftf3foI<5?J|2Lb`SRaJ#;1?~RQ{(fGA zhXi*72v-e`M;Z`8r4JMpnMj?=4F*L-W1I^k91oJ9Zii@=CIS%`3&Lt0j+yUdvz9#Lp}oZda?D_pO6> zn~9GuFO!a(fP)W(v55P!kNz7#-Su~uRT zTgGVgxn#r0f%Dc*ilxpifLTa4zW4B-HcuhMR=^KcikcEmBod#-4OWd6P?aocm3_1~ zRW8fQO$`Z>CIk5JoKmKMg#{0^8`EG=OTrXF^j>JGK zY$HpDaPM1}aBA?FI_)!@#KE;y2mrV>7Rmg#mqL2Cy}dE3wA_ry`d2|legWM!PI#!5 z;g)J>AQdr)bI=Lr<7T%6cRc_+3a(H<8T3mH!J&R)KyRh7pkNP#D=alFh!oi(U#%Yb zxQk8~T_BD%;SM&@fD|4`=27n-dxXVCB`-$}O7wG-+}`Acj)rqZ{!$^R1NVGqSzr*% zTpYFRS9tthb_rLrTmXCG(~rufi^2X>fygX`F&9oi!ABcpRmgj|Wz?kA;ewtxXV zmn$0$qH$wKhcjmX#5LICq=c2-&x8UV(*qh zqGF^gE_9<=uD#*(16FvO8<_#2(`EJRfiX(eIKC)^D!X^Nj#sVo?W+QAGNFI@zL1i`8`GT`HAUrX_ZfH z4NCYQiXqca(*1w08~-oLHwJoo_WzGn$;ia^uhrOxKhihNHpKs^u?^n^?P)s!`+zcF z=g`dl=&`|D2jH`_p$Z^V{a~;@pFe}=Zr89p9Xol&o6HbKh%A1v2N*t%yZZ9>eRZXr za27&NnBj<{gP2hxw7*#MThp-|orO65g!jb}?}s0di)0Yg55(*OC9;>7-R^>qv5GSv z35o26q}I#7jHpNUk3F(GItyB_63bXA{Osu0kkya~nU;DV5GtdLLr}4F0h89%?BZ93 zZc-17l=_falnd$=<3&(Q1dBDxWSUaFSlFfGYDGv&f~330WGq=|Dq6+L<`hPb zk;LDWRhi4af?g*Xzuvwo@WQJ5vsHBfEN@pzr!J%guND!OTQv%HXOzd1%uTmRrF1ki zya8fmqC}Nj)B=eycEpcVmTLxkS}|s!B1a@|C}La^lSZ?$pISXKlvBnMuEt&t$_oeC zA29hRc(h@)L*A{j&>5oJxy1+$8YQ02g! zCalGRCU<6GvEPNtcX&=yZE9I(T&G%PVDb|9NGRa`*9Q zI-|$R$4&TheDG-aM*ydqI!6U3F4gxNSTu~sh4E;|5ODeVY81KUb=3N+(X-7l!}JP% zpu?sohu8T#-R1p9i}l;q>;i6cc)TT~%iEpxGNhsf53Zxa6y0ZDTEH1%y*7v=#0NVB zp;)Iqe$GWw8~f{|`~3>&dwDi!_ktf5o~r1~-bhO+(nMQ01xXOzcX@*eP>jQluh=FL zkO+8cEWCZ;fGGM~{+D0raGgzipi_%BZO8Nc*KandmPZVi>+NgWZ6{5fx4Gj&!%MMw zwE4f{?O{XA(KyfNVQ^MYfaG>4JYefS?vW?Run92ha5BK)XJI?P1r6~;E<@VOPc*#v zOT#C40h0lwFPS0S*_UnOymQxF6YkL97?b_DYO@i;spu453s*0TVG&~nK?#Qa+pA}AX7BN5Ib?kIg09K%Bc%o8D)V6;a815gJTv?Oe>WP{hvCgYLgCRo3=k?`a*h^fTckK%a2{|8UpNi*H(n0O42CNhI1?iZ}1AougdCze}=)L6-eZJCgS<-XX?$gU&2CUXvTOOUrnoL>+>} zq^5lwsYIPeLG@UV)1nA|kuP6jz77xNSrs!RblO4y3Gd362#Qu3Q# zWcE+fsOpH_>+NR8@-t(>7lTP+5pQ77L_AXJJfn$KBON*KF9t;#_Grm8!=Q-7^@(ZM zl#u4O>A0=C)4bzSm-{fGTfF^`AbFuYeje{X&x>gy7m!R*jLJu3z0yUqYIgT~7Hl8P zeLFVyd#jEu?i}8nt;;RmC!kgyb+3R9(kUTbO+0J&N0jfI!xj2#c$x&(7q;^+&7SS= z2kEYZP?eMPMMyy4A4kHjLE^o1k0aaIPBD*~z9WAvF7uG(P4crtF-`nU{7nV_qiJH@ zd*l@D??ej1u|jh+cgv_+Ph@0{pu!6i73uUJ(5w+o3cZs#p%!kU1>8 zk%qL7!3jWhhN7fNUgllLO5m`ob(M@lcuDIkJXf;m^QOLr%7$u}q0w+hUV%SZzjR&` z`ql#sfKs)zhI%)v(FZ`m%lhcO>#=niV7Ut^W|?)Q?glouH9x9VM{D6OJP9wBX~U>X zCKLFf^otQ3e?)K#MgGn~XN201de?XYQNF+00FA*?x|=o=odRM(bFA{H4*C&U3A`WL zl=QD{zOef)PuV72{a>urf>A;yS!asG4#rV~WgSOK^A;@>+$2WIQB#V7&0%gvTfSfFBN_b(3R-cvZ}A0NJ?fkT`iR}iW>4$ zd&R0^;tp>vYf58il~*d16c?@zbRt^@Ur3^BBe>CZo@>IR#0t>|wIS?Lq}RqsbxEPq z?#Sgc{WfUgYlEZENE&LCw6?cKxjmpu04r#@YkB?_&Sv{J>~%G~DC-|hUDRh-ZrV;- ztnzHNfEcOeQ?ez*s2_qnk7(`uhK*%)AFW7E+B9R2a+u2U1V%m~(=^35@0=zyp@uC) zj8+#y&g?TR{1q@#8faau%Q1Ud((D zUd%_+ePr46qRyn%qI2JlxEN`j)^F72LbLJH7f*lCAp-np0u?Q5^m4}v^Q)S$PPjGO zQP;`&T!l$ixIAw&{0fr6{r07g=uC86>B-7RVt!3wQ+YzxN2)ihB-0o*KSN!g>1+*B zmo^t18Bavk4zZzr&}IKgn)-*^8#5cj|G2&VKNe|@pDW$JZDlW0{tFWneEoq6{U8Xq z=;GRzBeec|e@WU8_w3{d9gwEl80Z5^eDJAfnz}7fD7app__FUr9Erv2c{9y)MBU#d zGk^S%R|eS zscGx0IvC71f^N(a>{x5%13msg+!1&3n?`WTa&ie#IwE2GX_YhN^wpV(S2N=PbCPYM zN$9U$Ldvv40|8x##6G(}_up9~pJ)@Zg!06u`Jv7Yzt>wRtgk#6#w|dvX&B_|FndCr zu&h^)j%~&S>`p5u!?~J!s5}D`%=YhLH-ySQp~4ukaK)@F8q+rbb43Fb(kV-5dm-egtZRF;+os{8|n`lMa=BBMjMj!3f;WBp^wl?oyx72k&Qb6U>tmf^SuePQ9$7d$S_eqspV^_^N( zvvPyZ&Ft_a_d^?Rzft2>n8_&zEZMu5(!WsYMzCg7`*B~rsi{IVXj%{$PwoMop*M0m zUb@Q-NFDAeedK2*@*}gY#Hur#!?c*&M@wx!QX90cpLU!`&bIe!mpXybWiYh;(7V#{ zw$JW4vwSh%@I`n*i4Zu3%L*!Ac5hCCL$aePO{th;5qOwqE0`oqL9KAkP`h)`5M zW>!t1H=+Y4AIccn4Lo?Y;Sp$Y_5=N}=X@Q)v1DzLm_TFD*=C?i8+UHPiXNO>ouoai zX4X+1!Kr=C9IIYm4*F=1aeDx=VrsnpsX+W#l>WQD`+pe<931~15UL>^^Roo{QInpa ztnep~+lEEtlcyWq{{a(2&S@VNsB6*zZtiS4Bz=9xktmdsNVFQy!1Y*dqP7TTx_@~I zW{gHdjMj@d8PJ@t2TL8)}Us4%^3I`O*!p2*Bm1Rr9oedaw8{HfXM%Kd-(~6*hl(bxfObZW_m1 zk}bFFtd0f>*&~UnCJt0mz2{J%ZV}CHCLuU&ot zpL2>HJ4f!bALQ)qt%$=A;~MFjsisho&?zM$6a-zs(-KF{Adz-P&>;iGbAZuNFs4~w z0|B^qDCPH!b$rD14%S~?nb5C}59qy2gF%Lm8eMaP7TzlXa)HADNOx^tVQhGTXZgX9 z)O;3<1Xt*7R8RNldhu@=y+_<8#urLkCjdo>P4xVU2HWyrp&bTr1_SaURo_=FIHu=1 z+cVc$DYu;(H(eE}-URIze zjH3iDXtdHx&~2C-!_NQ=MSOBoRcLzDva!DA??<}_y6_6hR!L@sD4x~Ngl(Ca+p}oe zm{wTXxsOd_G38F64s)s=KKisw{5n$i>vH}g&64B(GR;rZ-zfLJSmNX1c99>&PYbzFI%PI*t{|WM)ok#*KDqY8 zq_(lNhH6^WO##G4Q*^Wi^&`pzP*ey62QY($hhugrdeJ(XaJaUXp9AI{( zku8A0K)$=MjVXb|A{d&y7j0($%hEcfu@HxY@KQ`}4N*9@FrQ8>$YAj8$tVmYLC0y6 z3N!#A5Xr5`?r%2e_ym$j039v94J?4D!l#NmPnf6X>O)a4!-{+`eN~HRHoFDd*qi>( zf^-we52{3}={7d^P=Qdc(%YXE3@0!_W16=LvdRn8%tS4vbs2=#)qDk(3b`nqHmyWQ zjG@#As+br>gdO32e+dY;)FM|@7Y`~>A1Zq|1^a)q1YqicVy3 znhpO}e}$5(6v(z6MN$X?gMQm33j{&12eKbzFY|ff%?oSO8y>hnRD92liDd)Z!U;y! z*5ute`B$0onaDM`B?x^{s9n`k=JuJ^gNbQN^SS0`gn(mMnB*Oy(A^9>wg}@5VR~1n zDYO|)oOOH(eUW)D{(^?V62ANNPEt6*=$vYo3jBh}fK#EZsf@2eoe$nV+d8Uky;46~ zv61czy*Oz`SE?yUJI3fc&=aGZNzzSUS$aH#@_FyO{uJgWj7oY*oGJBA#HjP^6jy6<*6aiGZm|C*3UV zlW@#?J2h57FQyRf zfOk=LBhX}@$8oizV^=hvgvd!=M3DvP`6s`~5Gc^?g#g)I;mXmi7U{_aB2!(g(^?)8 z`f4vgan9xxBSAPJ1d^^72xk0TPpbGMVV$mWI!Lf*iJ!90_sv|`qg~chF*9?CPF+l$ z4rm+v@4DZ5${{%`$v}5<;KFjFu;>ZQyr$$|kk{#V3j81hbAY_kv6LKuUV__T0;ZkM ztQ0nPu#UK4c$Z%(cGivgkM5!CTwMUHH~b*yZ1>ooXzTTB!G#w#50LfXS*5aXkZai{ zjL|)-6=j>83T#u~>EE=TZSUP5_x=tmr9M&5l~)-11QL4Iy1B4hWB-AK+^6f`6K18P z_V+o2^aLU=zhi!8M+)J90h)C_rC5`}hI7OX%sc-;htmGy0_q1-b}_uUkkh!4=S1*9 z1fpY<2b8l2%YGOZAOR}#0r8XjW$qcm3jpVc4w8G$_ySiC9Wro)@HdYOeDhsRYcd7X z^ii|q=Jjzct)j*G+LeU@N-Djk=phDb?t_3PT&2NqtG!>9`DP(cnDKbmW`K<^vpUXZ zBPXjJ&Fpbhv68m`Q<#);W58P^26sXsFS=I;AXkR}6W>)}h6@gU(goo#zza;^>g8hS zr6mMLuiF}6%bO>|hAGG8RPtLdEA4#Kkkb(j0FEC6H>z&X!z`#a<&&S6jf)1sA5gB~ z*Y1lK@{?MgwFLge6r*v>3rT+6!4xFlFEI(n_FK;J25XI#U)6_}F?dgMJsXa`sd(v! zlZG+AKM|N{p8s$?V4?pf)CLpVzg9h7s!RUIZ1RIxI#CkipS<~m2*+*Dm^7IEo$;1D zDL};(EvG4C-M0Gi4ktX;Y%~bJO5s6{7NLgp=smE@#)J(engAi%5}+#*P$Andgo#0+ z&g>D1BBC)Sh7nF2CbECROA0~;UqFYL0AfD4GkEqCV&1Mr`n%le!#=ypq?vr4G3S@f z#f}N{izUAvdGrXTqGcgPO#SN47oe!2+Y?EDCz&1GhO50aN zsU6>D&YH)ne%FB*<>_T-!=Z&dHC4l!n`(}q)jeksM0JP|(L|81=Y!EFwynx4Gv0dR zP0GHPda*DXu1~|Eq@bOZwVT#b&Fj0P?=AOqghLtvB9W=g_~QV74f|ZCr$^Kd>4#)6 zh#|HwOeNbX5CX%D*C5b<@80dkMx1;qJ2y5gMrA4l6UF6)dwTLxjFO2ZF)(J{SFUL+Rc=jUrYcys2>=_(c<7db2{J;u~YP!vSZ(s+?Za|=Z6 zCRVYrUfY3KxErZZRO-C17#&5GK-OXyz>v(|Om_i3H#5chSptD)m|UNzq&n3@$sa3+ z&-ATyPo1fqcy9EW-6gw?z?$TL8%u{(c0q1QmA$S}Uw5|655fub0do8Bp>8F9S63#P zrc`m$+nej@&pV_ZMp#+Ij1%IF7QFGm1DA96YUAE4R~YL^X-wqIgD?(mL!vT(2BkG5%lW8(b5CvQL^@0_o6Css+xiQPz>pzu=Y3OT$T9z3EUyp z669}bj*)+2T8l^9!k*!5MiJ1wP1k3*;sE+I?yJfIDDgDseuz=p)Z;XtZ#nMTLe>z_ z9Tee{kn|?G@i{i%)kXk-RQ2%w{V)tBo<^={!OJ*owJZb=K>hne{Gdf4LCXpCitey; zRTekR4<7Zd7Bzj{q`tPdS-b%_hwKnCE9z%l@IwizG%dzx!jI+1kvT?v=lNwECnt>88~i76=xr`_Q_RdH-j~ z;WJJ4c=4)zx(%>m8ukKY$urR`w_S4BT?5m>=M!fm6k&&F{>PnyRB z+||)L<}zYa2WKVSo586vgLdJ$QsCqeOBXTfJBc^bEUYo__+i&n;KdqH z9^vjoFRITUX9X1jG)w?@HssmxVgrSlT^k+L+bPzO90@_`b0otPs1H%TUA()ROLL5; zdq)xOd}WxWBOCyv`_~`xFfr#97lu&e@h+}bP4KCpvCri7j=2aNytuA2PB;oFhRrQm zX5P9+nVWKMMY0a7d+npaKP9^c2GHm3%SToY>zvZ=Gp9~hI^OCf49ilom(ByyaqFz+ z$|Qtl(SmWgN{yMT@?k--x0=Fi1a<4@b~jLAIoUZ}DaH4Ch)-dWnRo7GUmqlh%DJ9u zhH!l(ddI-#l4Cc3r%N0*`;E3X&bsIDx>MTQx@6U~L(OJno4x=Jaq8LsRIh&wF8^mc z$HMflZ7)q~>oz}FXzyiJ1abs9{?=74@jRNH74bCyzzreqdfT|m26W< zSomWpxR1c9G+}nv1tfh}n{xr%oe?ARb zj%C2llrEeMB!?(KnqnR3rjq~zg9q)4w;S*P@8!>2qkedS=W!iyN4m5ReLX7pO|G&= zQZ91;skV8vRw}(i$3-~WpCJh_TNo^V+#46n4#1h?AO)A>Z$&Zq<>b{Vyu?O9m$%syEHe$Z9}d4E*eK6EXPn#$Giz=Cu}mAkA*j^-R`Caix*|B#J8?rhWJoN>>cwocvk?j{Qop0sAx z^43}goQoVv5IH}KA+#qkKBf0XpT;xCoM8>ns`O~uXu_?WQbC!ep*kNm)L94J3P+ePHZ1s0h$%3zjM3_2*d2ZQ&tq(z7HwN2>w$Qz! zEQ%jSCn&9yn2)s-yn$1)sGEdYOhc>D?#$PeMcIJ^LiwBP^ME^+)LHPlDM1qBpK=2| zrma#kD7N_wyw6?L0_YbB^$S2$B7Vdj*lXA>NKpa&*aZA%C&5B^@BXaNroe2ri1dPR zX4?(Hysv=?1R8)HyJh$os88wo0reiv5!?K|lQGUtD11GqH4HMsIB=3gi5K53?ZCb_ zntrBAhXm?}d$rD&sslLQ;H(tgsnjnb)Pn%3#t=n-aVF;oiL!lM%`*en4870F&Yi(J6hs1ou z8}1I3MaP2xeTg%e0Gc~mPFs;O8Gyn}cmnEcR{lQ06bWO3)0>2S``I)sB&28wjV8yg zsb$uphpz8CIJmLB?=UWm?&=;MvqII#=19-HNKfbs?t>X@t=OTl_~))rg-ug$TB)uj zIt4vs$7%_>(S+W_)2HdJ?n|gpQ#p#n!Y%;>0eZN98DYnC7C_^8(a-_H22LK+STVY_ z>1+kGb`BZ00TqnG@(6)RdD7@hFKH!7q{4;kgxF*lFFH`p$r=!xi%zBTMK@_wbu-!} z{%Bg+f5+|Y+MSG#8n{yj&Vt{0zR59o;YOShVF$Ni`O}IsM&2E6-E?96Z3#s39_HZY zb>Yc=O;G(wc3gw%&h#+TTmWyH7m^2{WF*F``YN*1p68?>+s8cfy?E392ZkBD(fq4` zkr>e?=|FxfX^TA4IUEjOR#oOLLxYdMC{M}7DMf4PS~-qq>Bpb$@O$+%&#JS&aq!_! zHA-#2kM);aOv5v2Xmvtin6f*sji^pZ~rW* zAz?cj`}Spew%eRUPFlA6$OQ;()9cUaN&L_^72bz3QE+s|5sK=YHC5L(X34Ge?pA(< zwbH9dT%~8BGKewPcgTtvb8#OS3))yN_D3GCku+bhcbK8HBzYpQ}_p}$T ze0X_WuM=0c2k{^Rh^}JE0sYpt3>g`=0 zXL)fCkF1nP84?2Upv>D9j$I>&}S6+W9N`fUp2 z<)-v@N0cv;=OU?_p(YR|At$OGR3J)w0WK4Or4bhjhb2ylA3q^S8J8YhqkhkJrJrYw zIIbe}oGLVvJ`h|!N%M)X`J1)*+Qzd^R-|(}7ot;Hm}pT zhS5dG{ilbT2U`^EP0sU@`v9N*Q1V2b-K!GGwvj|L7C>L(TyvQTuO#Bi4yuniY|fae zQ+!)y_Q;?K;h_IlG4t>b?wxXdS5Dzxoo&3u>z4M~Z}&66=JbMLooLTMdUJLtm6@#o z)y^QEFyzFgsu!}0t&@)$QoqyBh00&^n z@PkrzW;!5WU0$>&=i`lrm2d8Nu6=N?&TCV_MDZ?H-j zNuPq#tk@lMsTR+xtgt&zcUL}5kjPzZA1KZMU;j4jmz&6br>#sZ!df9kA z^Xw{s^}FQh@Rq`DBVh^iVx9^R{~B`@{B9R);r85pV>|(wkPX<{LP*k6hWJ~o^O}`+ zIqMVOLim{71AwQ3jK|bx$O~Df)o{<@5*`S!5WYzVx?7xaRn#q~*aPQbjrh#vRR9`$ zGmX8$vevMb9#}j8Evg$UTrC)Sw+9WESKmm*2Px3$IeiRdyC_)44-HU~0Z|rcogxw5 zo?Ua3cMdBo8_s+{MA`aHS%dW_tk@Mq7Ko=aWe_}~CXKahAXbF+wX>4T>Odj#MgU;$&>sI*4?V@VL zqSTc>WkJB30|Y8NxPI`UQ)Bl0;9LmXr^2QJI>2}AMJt}AbZKQWx5hd;IIWzkPq()4 zHv6;xR@?s%q}~6T{QQ3)>lW3wqu1Esy^gC#<{=99-o&hiPn;Cit*MmDoSF}<+ck`Z z^oRrm<7YN6b-nl5A?)dE_%%0qYSMddCf$ziC(!&m1A{xG{hQ}|&wm(1fC_^2ww%wz zqQ3B1O2`m<(xzLJ^!y8Wk)_Z>FIF@x)`tpqA+!_ft=d52hZmcpC7&O)H|Xk}0~Oely`)yFYB2lY4hI zT@v|>qjS|MhHeP;E|CBfy~08UwiDoxEw(dbCXI=ON;s0BSuGcdeSL@_V0jGW*~|8r zRrnG>eQBHk9CD!bI{MT-wQ(cQfo!B86toe!CUS@vE7$6b%TW@SPwnUK+ zWZI~@D(OFTg1^}b!*Dd1gogsoW~YJVL!iEAD4o~VI%G)YyWLGn+k)Q4Vut~EINEs4M}xJWY#71=CoAmw|KRuPVaZP9a8>)uL0ht9Iovu;Yl6N zTWQDB|JJGxnya%D%GJQMtn~ra`hEz)+CsD0=%yxYR@@(O#k?-*^`NfXkc<$DDbWEp z$@0T2ycZ(?j|rzUR&nva!e1XJvzC+?*_V9bmz^qaT1l#Jn$xM<*r@13sLqhZlih8UCzJPulDdqK|8KQVy4CD z>x*2RX#zEQ_(VU@5QmzrFHHx|;6!-%x+TNRrqDuawe1$&=3hh=pdg>N9ghwNX{OoC8q)i{QVP?>PP4hvx%m^@Gt}!%Kr)N?vFy&3w7d|CCS#=F0+~G0XIAi z)i-%-18+->d!1XmnA*>}cGgtF(H=S`tuwl;dgMPGOzcjdEP+YGR(};>ci$=QW#$8U z2%jx0GenC9*r%F2`)U+o;=bZP5~5N|rNI%RlG0ivv&>My)>u5F;=k%#1b(HjMRc)D z`1lU~CQQXn9mriZNdo;i)S)s`AwQyyixv0?q(1TeUYBw`ws%^+Fo1q`I=-C~Dk6r| z%(9NqFPOvsxIjKpkk`OyRjSZ#1ZGsaGB#J<5^`sxZPKcU^o}S$`Xm}#@HmJEE2~BL zGa>oGdA=fWY0UsYh|r;Q!jlD4=n30a!fr3lITYu&CYlkU_GLCb8|aUny!IvdnkZ>$ z6H3wf(Z{EsUh%)g7Qx8!@$xaYVgOe${rT&VeLBa{N>j|zDviL>kHY|tcRG-X<~Jyi z7Y)+quh5f=vhj-ylj^j4HB#c@TY?T7IC6bErgNp2xR)GO{l z(pDC0@1}K?RnD4r>)6p|`!=Wij_#2tvRE(VOt}xAPk+(E`w7z)62dNHu?d;rK>YH3 zqQx3qObQGYN33gmMJOrrg~&pQe!~3CRz6!~Q4B?2MlZ$F@4$nCc?!E>;I<;6{%zi% z(7y7e(@tZ8ir_kpJ|0}aQMvlna$BOQr?ySK$ILn`c)68!yjs>A4SnK}dRoofB*vSQ zbIO>tQk)~)GhZ2i!bEKUN&J+kuyqast6)bPr0g~9%sA8UerbhI>p8;F_tUlVki6## z2Y`U%USVZ~_Z3`d{VG9fkd|>qe%fdN%uFgiej}@Dpws{UCBQRxv@U~;$)`m7f|zJC#uLGZeNQ6PBXCs`Om70`{Sr35Olc$kOJ&iBy4hQ zEXu$K#|>0OT83X43#+!_hDxnz!~(Jh4n#YmIEuYgelfOzLXTpx2p}nbKPwsAbToy~ zWO(4`0g_E@Vh$Beq#%>TDdxz7z0uM#nLSvnw)TA7|2VE?8|5S=IfX%;~GDt7Px(Rkm(N5U5%!Dz-vq$@(P)K1*tDWWHfgKtqr4QRP=L;sZWU$i#WL5#*_hIh2(WCVezG?h9==HR3@cv+{*=9WX6yY8 zw$zuNS1TNyd-Zm8cJ?{Qw#~e|yndePaobm^v(B~eHFIv%OBDj4W~qi9YSh@r~2Y4y&wKh^12o~_gAQr^6OocW$NvF`1Sc`NiheQWTD z7#6wQt{J{ov^02jeCy)jIv+hG#B0`pH@sW-Zu7p{S&vZ~Cp$s|so2;x{C5*Z$3Q~n zC*`iAYkNEA`rct`(b(aNzb9M}x`OpZ1+y+mt73b#)c*9*fB4u-Su?osL&$q^b1N!) zH~#XnYTYvgZ*S(Zt^Is_vy!I+)Al`mT(c`_bj*QUIB$NFAC&NKzjb*7X_P;qteLzTtiQ)0l^N+RHVY+i6qTm zWqel)?`3~yba_2Fmx>QbAigIhwKR3+;5|hQT9cD;!SUkI!{Xse@b3d+L4Al#APo^H zzorn6kUJ7v)%yaJrG81NXFQQA!g0QPLb;1tv9A%q=jsG0B~LDXVzzZ?aw5 zpe;5I1?Tw6BZ)Ctmo+USdE+in0-O_*84r4cyhG9KK5;&!sY{xJGu|u(pdt8b#0r*U zoCC32^AmTyO0;3UG7lBV}h5LqYLw z^-2F5&xYhBX9dAACQvR$}+p)PF3&91k zimH;~HomP?UhN3HbA?MQ7nh|rGH&C_fI6%@ZhcYfqn}?5geDKwG?I=`I0c^+4@Fj*`$>)+m(Dg4SmpZeo3A)hELolu zh%|_7fK&i&Y9H(mHWI*ZT|L}Wu*W7Un#g~5pZ@A=!8Z8TVuJ^~NMvD@-{drk}XN=y+liazXgFw8= zO1EKv%x4Wbwt8>Wq=E=D#Ac=ntPblBQ&ig&y_op~XV7EZpda^gz#hPl=@8KueX*-Z z`64++wkcpX6*|)?4oMN6kQ%-8yrJSf>plprp#XH6pBqBks?L_)jM~XIBw)CF%lX;9 z<8$G0-_!8zayWYZ9PVTQ&R8uvH>ZC7(LH|753D{G@rLs^a~@NuvFt@z z(0=}IRJB?lTl>{farFNup?5re7n#N$&TiO>z3DJpx+~`_aPp#@?ZZvA%{31dKyY~Q_(^Rd- zXh-)t9IVqIfxVa^XBo>y-dS=YIAY7o;VKURPDOXn6K>S6N?D?4NH7rNxIca3i6i06 zvMW((O8Zj!>jU~lQy4Vu0jZqp!9i&zXV@^+$MNomh8OPDwqJ{?p@#HO4blViz+*EH z^1zfwNbP2jaJd&>3Oy2XtTN!N(%`JJJFK#NiKoSkzU%+<+b0R63WfH1r_MvdT#K0f zuKpP+u0y|#ZwUDt4y_&3$5Stos>8*`{i-w6q;N5PG~JBzFAoPD?TK`)qg11$EDeWo zd#VpUCmRo3_D6h<#+oyw-l5B3gq+8e=_C;tUfm;k&l708591h`zQg_i9_Hx_hNr2P^8)i~j6uGS^@dH;OyVa?ffj2fKi*|GXVd_en z!78me^~#N;H)u6S>W}oABZl2#o4?$Y9}AJu!vP!sUh~}Qb3RL6MRu_(JICC+0XGGy zPluE`=0%<`pGT>j%`O~SPNDnYoSXgv&$O-^Tss~>`kpL;Yr1_c(J|~iPz13BVBn=v zrjE>j`KNgV(r7TJcC&W19$0$}P)|&(!JF|SYfue*fm4OlN`_y+StWJv|8hlS`FGDP zGyQ)WU~g2j{&Bs)e_iSNI_dILu6qVYUdjBvSI~BrcdWctR1iV7#dbn}2VfiR?p9Z$ zM;FXseja7r@-%TdN;NgN8eR>4z4&aElL<*km`L5`1Qg9pkjWvB8%U+gph||n z@=W)3NFgSNMz^ZK%`T9acjJW-e0IXIRpq40XH+*exxBFAeRmbrqi_)q#|R=X*a%gv zFSlJ$@A1yYj!1#be786p+x^*^jtpIR<$Sjcdj23rrVtcf{%Jn5Wd(&mPLrVnmt(shR z-E$SSm-tYJfV~?vs^zRlUw&q>2@OT;B5F06=ON5f=ZwD(Yk zJd#b(GZ-=G@$jTUjkf83l&Y3)SGO~Z<AS;bMz#+ ze#;sc2m%lRy@6SS^Cb~y3sXEl^T?K2u^6v^=8>zMb$@JEYSnDJJ!+^Ig=8sAdbFP) zRf8#jz>7!~h0*1x5|GTjIELtrP$hkW>}*(!ypoaqF@ah`SO=Hv)_EzeofPHaxd}I{ zwOH3}$~fB?oR#>(7&XTv%$u{dzO2g~(+7*{py24s%E}?b{S}&EPf5A76LFSYIF4RB zdm}HR3#X(=4A7?ZdD z`!+?Kx9$q1chFW`4xtFR9QHLa1YL3M+0wV3Ja?b5(y@O>TqNjf+%61V29IDnn&Y;* z!<4Dw;vBA5NU6M?l$^n!={*gE=@ypNtllKqOGILn0mPRUt$()7-(*tNX!WpdP+SAT z0^0o2uRnS|*5|ymxRThgD9Z&UXLEuFVgQdZXmXBVX2EX+N}4a5f1wsm`SrQY~4_xTFrp!!EbG)$0)g2 zNsH$GsI9q_-TZ5TOuBjoYs~5V%q}bH)>sutwTTl1^r{c+)jGEvF|5hF)ilSx$^u2r z?+&X-URYrCZ;tO?q@63iRrVC;Z(tI-n((5ii{=`BX8V^^0`pC!;E)+o!{wCSScrj| z+`{qPT6gIj;yrq6sbTY6ZℑPH!w5ciN+XdMXIZTKwM-PP~o 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□ViZg0>~7ZXr7vf9b?OSt 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

  • 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�=QC5EX6}XjsHqgFqZ?^meJ3u6 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%0AYODn~X~V0Jij7kT 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