From 581672c6faf65a75d2951aeddb67458fd786b31e Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Sat, 25 May 2024 03:16:47 -0500 Subject: [PATCH 001/121] add cmake and build structure Co-Authored-By: Tejaswin Parthasarathy --- backend/cmake/.gitignore | 1 + backend/cmake/AddCxxFlag.cmake | 142 ++++++ backend/cmake/CheckCompilerVersion.cmake | 36 ++ .../ElasticaAddInterfaceLibraryHeaders.cmake | 98 ++++ backend/cmake/ElasticaSetupFlagsTarget.cmake | 7 + backend/cmake/EnableWarnings.cmake | 66 +++ backend/cmake/FindBlaze.cmake | 59 +++ backend/cmake/FindBlazeTensor.cmake | 61 +++ backend/cmake/FindFilesystem.cmake | 245 ++++++++++ backend/cmake/FindSleef.cmake | 147 ++++++ backend/cmake/FindTBB.cmake | 417 ++++++++++++++++++ backend/cmake/FindYamlCpp.cmake | 42 ++ backend/cmake/GetEnvPath.cmake | 15 + backend/cmake/Logging.cmake | 100 +++++ backend/cmake/PrintUsefulCMakeInfo.cmake | 63 +++ backend/cmake/SetBuildType.cmake | 76 ++++ backend/cmake/SetCxxStandard.cmake | 7 + backend/cmake/SetOutputDirectory.cmake | 27 ++ backend/cmake/SetupBlaze.cmake | 85 ++++ backend/cmake/SetupBlazeTensor.cmake | 34 ++ backend/cmake/SetupCxxFlags.cmake | 57 +++ backend/cmake/SetupElasticaInlining.cmake | 18 + backend/cmake/SetupFilesystem.cmake | 27 ++ backend/cmake/SetupHDF5.cmake | 43 ++ backend/cmake/SetupMacOsx.cmake | 19 + backend/cmake/SetupPic.cmake | 16 + backend/cmake/SetupPybind11.cmake | 34 ++ backend/cmake/SetupSanitizers.cmake | 72 +++ backend/cmake/SetupSleef.cmake | 28 ++ backend/cmake/SetupStl.cmake | 123 ++++++ backend/cmake/SetupTBB.cmake | 34 ++ backend/pyproject.toml | 50 +++ backend/setup.py | 139 ++++++ poetry.lock | 317 ++++++------- pyproject.toml | 4 +- 35 files changed, 2554 insertions(+), 155 deletions(-) create mode 100644 backend/cmake/.gitignore create mode 100644 backend/cmake/AddCxxFlag.cmake create mode 100644 backend/cmake/CheckCompilerVersion.cmake create mode 100644 backend/cmake/ElasticaAddInterfaceLibraryHeaders.cmake create mode 100644 backend/cmake/ElasticaSetupFlagsTarget.cmake create mode 100644 backend/cmake/EnableWarnings.cmake create mode 100644 backend/cmake/FindBlaze.cmake create mode 100644 backend/cmake/FindBlazeTensor.cmake create mode 100644 backend/cmake/FindFilesystem.cmake create mode 100644 backend/cmake/FindSleef.cmake create mode 100644 backend/cmake/FindTBB.cmake create mode 100644 backend/cmake/FindYamlCpp.cmake create mode 100644 backend/cmake/GetEnvPath.cmake create mode 100644 backend/cmake/Logging.cmake create mode 100644 backend/cmake/PrintUsefulCMakeInfo.cmake create mode 100644 backend/cmake/SetBuildType.cmake create mode 100644 backend/cmake/SetCxxStandard.cmake create mode 100644 backend/cmake/SetOutputDirectory.cmake create mode 100644 backend/cmake/SetupBlaze.cmake create mode 100644 backend/cmake/SetupBlazeTensor.cmake create mode 100644 backend/cmake/SetupCxxFlags.cmake create mode 100644 backend/cmake/SetupElasticaInlining.cmake create mode 100644 backend/cmake/SetupFilesystem.cmake create mode 100644 backend/cmake/SetupHDF5.cmake create mode 100644 backend/cmake/SetupMacOsx.cmake create mode 100644 backend/cmake/SetupPic.cmake create mode 100644 backend/cmake/SetupPybind11.cmake create mode 100644 backend/cmake/SetupSanitizers.cmake create mode 100644 backend/cmake/SetupSleef.cmake create mode 100644 backend/cmake/SetupStl.cmake create mode 100644 backend/cmake/SetupTBB.cmake create mode 100644 backend/pyproject.toml create mode 100644 backend/setup.py diff --git a/backend/cmake/.gitignore b/backend/cmake/.gitignore new file mode 100644 index 000000000..0d20b6487 --- /dev/null +++ b/backend/cmake/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/backend/cmake/AddCxxFlag.cmake b/backend/cmake/AddCxxFlag.cmake new file mode 100644 index 000000000..2d1085f44 --- /dev/null +++ b/backend/cmake/AddCxxFlag.cmake @@ -0,0 +1,142 @@ +# Checks if CXX flag is valid for the compiler and adds it + +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +# Distributed under the MIT License. See LICENSE for details. + +# Checks if a CXX flag is supported by the compiler and creates the target +# TARGET_NAME whose INTERFACE_COMPILE_OPTIONS are set to the FLAG_TO_CHECK +# - FLAG_TO_CHECK: the CXX flag to add if the compiler supports it +# - TARGET_NAME: the name of the target whose INTERFACE_COMPILE_OPTIONS are +# set +function(create_cxx_flag_target FLAG_TO_CHECK TARGET_NAME) + # In order to check for a -Wno-* flag in gcc, you have to check the + # -W* version instead. See http://gcc.gnu.org/wiki/FAQ#wnowarning + string(REGEX REPLACE ^-Wno- -W POSITIVE_FLAG_TO_CHECK ${FLAG_TO_CHECK}) + execute_process( + COMMAND + bash -c + "LC_ALL=POSIX ${CMAKE_CXX_COMPILER} -Werror ${POSITIVE_FLAG_TO_CHECK} \ +-x c++ -c - <<< \"\" -o /dev/null" + RESULT_VARIABLE RESULT + ERROR_VARIABLE ERROR_FROM_COMPILATION + OUTPUT_QUIET) + add_library(${TARGET_NAME} INTERFACE) + if (${RESULT} EQUAL 0) + set_property(TARGET ${TARGET_NAME} + APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS $<$:${FLAG_TO_CHECK}>) + endif (${RESULT} EQUAL 0) +endfunction() + +# Checks which of the CXX FLAGS_TO_CHECK are supported by the compiler +# and creates the target TARGET_NAME whose INTERFACE_COMPILE_OPTIONS +# are set to the FLAGS_TO_CHECK that are supported. If adding many flags, +# this will be much faster than calling create_cxx_flags_target multiple times. +# - FLAGS_TO_CHECK: a semicolon separated string of CXX flags to try to add +# for compilation. +# - TARGET_NAME: the name of the target whose INTERFACE_COMPILE_OPTIONS are +# set +function(create_cxx_flags_target FLAGS_TO_CHECK TARGET_NAME) + # In order to check for a -Wno-* flag in gcc, you have to check the + # -W* version instead. See http://gcc.gnu.org/wiki/FAQ#wnowarning + set(POSITIVE_FLAGS_TO_CHECK) + foreach (FLAG_TO_CHECK ${FLAGS_TO_CHECK}) + string(REGEX REPLACE ^-Wno- -W POSITIVE_FLAG_TO_CHECK ${FLAG_TO_CHECK}) + list(APPEND POSITIVE_FLAGS_TO_CHECK ${POSITIVE_FLAG_TO_CHECK}) + endforeach () + string(REPLACE ";" " " + POSITIVE_FLAGS_WITH_SPACES "${POSITIVE_FLAGS_TO_CHECK}") + execute_process( + COMMAND + bash -c + "LC_ALL=POSIX ${CMAKE_CXX_COMPILER} -Werror ${POSITIVE_FLAGS_WITH_SPACES} \ +-x c++ -c - <<< \"\" -o /dev/null" + RESULT_VARIABLE RESULT + ERROR_VARIABLE ERROR_FROM_COMPILATION + OUTPUT_QUIET) + + add_library(${TARGET_NAME} INTERFACE) + if (${RESULT} EQUAL 0) + set_property(TARGET ${TARGET_NAME} + APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS $<$:${FLAGS_TO_CHECK}>) + else (${RESULT} EQUAL 0) + # Check each flag to see if it was marked as "invalid" in the output + unset(FLAGS_TO_ADD) + foreach (FLAG ${POSITIVE_FLAGS_TO_CHECK}) + string(FIND "${ERROR_FROM_COMPILATION}" "'${FLAG}'" FOUND_POS) + if (${FOUND_POS} EQUAL -1) + # For some reason: + # list(FIND ${POSITIVE_FLAGS_TO_CHECK} ${FLAG} INDEX_OF_FLAG) + # doesn't work with some compilers. This makes no sense but such is + # life. As a work around we basically implement a find manually. + + # Find the index of the current flag in the POSITIVE_FLAGS_TO_CHECK + # list. This is the index we use to get the original flag in the + # FLAGS_TO_CHECK list. + set(INDEX 0) + foreach (POS_FLAG ${POSITIVE_FLAGS_TO_CHECK}) + if ("${POS_FLAG}" STREQUAL "${FLAG}") + break() + endif () + MATH(EXPR INDEX "${INDEX}+1") + endforeach () + set(TARGET_INDEX ${INDEX}) + set(INDEX 0) + # Get original flag + set(NEW_FLAG "") + foreach (ORIGINAL_FLAG ${FLAGS_TO_CHECK}) + if (${INDEX} EQUAL ${TARGET_INDEX}) + set(NEW_FLAG ${ORIGINAL_FLAG}) + break() + endif () + MATH(EXPR INDEX "${INDEX}+1") + endforeach () + # Add the flag to the list of flags to add. + set(FLAGS_TO_ADD "${FLAGS_TO_ADD};${NEW_FLAG}") + endif (${FOUND_POS} EQUAL -1) + endforeach (FLAG ${FLAGS_TO_CHECK}) + set_property(TARGET ${TARGET_NAME} + APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS $<$:${FLAGS_TO_ADD}>) + + endif (${RESULT} EQUAL 0) +endfunction() + +set(CMAKE_SUPPORTS_LINK_OPTIONS OFF) +if (CMAKE_VERSION VERSION_EQUAL 3.13 OR CMAKE_VERSION VERSION_GREATER 3.13) + set(CMAKE_SUPPORTS_LINK_OPTIONS ON) +endif (CMAKE_VERSION VERSION_EQUAL 3.13 OR CMAKE_VERSION VERSION_GREATER 3.13) + +if (CMAKE_SUPPORTS_LINK_OPTIONS) + # Creates a target named TARGET_NAME that, if the linker flag FLAG_TO_CHECK + # is supported, defines ${FLAG_TO_CHECK} as an INTERFACE_LINK_OPTION + function(create_cxx_link_flag_target FLAG_TO_CHECK TARGET_NAME) + include(CheckCxxLinkerFlag) + unset(CXX_LINKER_FLAG_WORKS CACHE) + set(CMAKE_REQUIRED_QUIET 1) + check_cxx_linker_flag(${FLAG_TO_CHECK} CXX_LINKER_FLAG_WORKS) + unset(CMAKE_REQUIRED_QUIET) + + add_library(${TARGET_NAME} INTERFACE) + if (CXX_LINKER_FLAG_WORKS) + set_property(TARGET ${TARGET_NAME} + APPEND PROPERTY + INTERFACE_LINK_OPTIONS $<$:${FLAG_TO_CHECK}>) + endif () + endfunction() +endif (CMAKE_SUPPORTS_LINK_OPTIONS) + +# Checks if a flag is supported by the linker and adds it if it is +function(check_and_add_cxx_link_flag FLAG_TO_CHECK) + include(CheckCxxLinkerFlag) + unset(CXX_LINKER_FLAG_WORKS CACHE) + set(CMAKE_REQUIRED_QUIET 1) + check_cxx_linker_flag(${FLAG_TO_CHECK} CXX_LINKER_FLAG_WORKS) + unset(CMAKE_REQUIRED_QUIET) + if (CXX_LINKER_FLAG_WORKS) + set(CMAKE_CXX_LINK_FLAGS + "${CMAKE_CXX_LINK_FLAGS} ${FLAG_TO_CHECK}" PARENT_SCOPE) + endif () +endfunction() diff --git a/backend/cmake/CheckCompilerVersion.cmake b/backend/cmake/CheckCompilerVersion.cmake new file mode 100644 index 000000000..ba4056200 --- /dev/null +++ b/backend/cmake/CheckCompilerVersion.cmake @@ -0,0 +1,36 @@ +# Checks supported compiler versions for C++14 + +# Distributed under the MIT License. See LICENSE for details. +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.2) + message(FATAL_ERROR "GCC version must be at least 5.2") + endif() +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5) + message(FATAL_ERROR "Clang version must be at least 3.5") + endif() +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") + message(FATAL_ERROR "Intel compiler is not supported.") +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) + message(FATAL_ERROR "AppleClang version must be at least 6.0") + endif() +else() + message( + WARNING + "The compiler ${CMAKE_CXX_COMPILER_ID} is unsupported compiler! " + "Compilation has only been tested with Clang, and GCC.") +endif() + +# cmake-format: off +set(CXX_COMPILER_CONFIG ${CMAKE_CXX_COMPILER_ID}-${CMAKE_CXX_COMPILER_VERSION}) +string(TOLOWER ${CXX_COMPILER_CONFIG} CXX_COMPILER_CONFIG) +log_info("CXX compiler info") +log_info("------------------") +log_info("CMAKE_CXX_COMPILER ${CMAKE_CXX_COMPILER}") +log_info("CMAKE_CXX_COMPILER_ID ${CMAKE_CXX_COMPILER_ID}") +log_info("CMAKE_CXX_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}") +log_info("CXX_COMPILER_CONFIG ${CXX_COMPILER_CONFIG}") +# cmake-format: on diff --git a/backend/cmake/ElasticaAddInterfaceLibraryHeaders.cmake b/backend/cmake/ElasticaAddInterfaceLibraryHeaders.cmake new file mode 100644 index 000000000..10b7d3176 --- /dev/null +++ b/backend/cmake/ElasticaAddInterfaceLibraryHeaders.cmake @@ -0,0 +1,98 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +# Adds the header files to the target. +# +# Usage: +# +# add_interface_lib_headers( +# TARGET TARGET_NAME +# HEADERS +# A.hpp +# B.hpp +# C.hpp +# ) +# +# This function is intended to be used with libraries added using add_library +# or added by CMake's provided find_package (e.g. Boost). The +# add_spectre_library handles adding header files for targets correctly and +# so this function does not need to be used for libraries added with +# add_spectre_library. +function(add_interface_lib_headers) + cmake_parse_arguments( + ARG "" "TARGET" "HEADERS" + ${ARGN}) + + if (NOT TARGET ${ARG_TARGET}) + message(FATAL_ERROR + "Unknown target '${ARG_TARGET}'" + ) + endif (NOT TARGET ${ARG_TARGET}) + + get_target_property( + TARGET_TYPE + ${ARG_TARGET} + TYPE + ) + if (NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY) + message(FATAL_ERROR + "The target '${ARG_TARGET}' is not an INTERFACE library and so " + "add_interface_lib_headers should not be used to add header files " + "to it." + ) + endif (NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY) + + get_property( + ELASTICA_INTERFACE_LIBRARY_HEADERS + GLOBAL + PROPERTY ELASTICA_INTERFACE_LIBRARY_HEADERS + ) + + # Switch to delimiting the headers by ':' because CMake uses ';' to delimit + # elements of a list. + string(REPLACE ";" ":" TARGET_HEADERS + "${ARG_TARGET}=${ARG_HEADERS}") + list(APPEND ELASTICA_INTERFACE_LIBRARY_HEADERS ${TARGET_HEADERS}) + + set_property( + GLOBAL PROPERTY ELASTICA_INTERFACE_LIBRARY_HEADERS + ${ELASTICA_INTERFACE_LIBRARY_HEADERS} + ) +endfunction(add_interface_lib_headers) + +# Returns a list of all the header files for the target `TARGET` +# by setting the variable with the name `${RESULT_NAME}` +# +# Usage: +# get_target_headers(MyTarget MY_TARGET_HEADERS) +function(get_target_headers TARGET RESULT_NAME) + get_target_property( + TARGET_TYPE + ${TARGET} + TYPE + ) + if (${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY) + get_property( + _ELASTICA_INTERFACE_LIBRARY_HEADERS + GLOBAL + PROPERTY ELASTICA_INTERFACE_LIBRARY_HEADERS + ) + foreach (_LIB ${_ELASTICA_INTERFACE_LIBRARY_HEADERS}) + string(REPLACE "${TARGET}=" "" _LIB_HEADERS ${_LIB}) + if (NOT ${_LIB} STREQUAL ${_LIB_HEADERS}) + string(REPLACE ":" ";" _LIB_HEADERS ${_LIB_HEADERS}) + set(${RESULT_NAME} ${_LIB_HEADERS} PARENT_SCOPE) + break() + endif (NOT ${_LIB} STREQUAL ${_LIB_HEADERS}) + endforeach (_LIB ${_ELASTICA_INTERFACE_LIBRARY_HEADERS}) + + else (${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY) + get_property( + _HEADER_FILES + TARGET ${TARGET} + PROPERTY PUBLIC_HEADER + ) + set(${RESULT_NAME} ${_HEADER_FILES} PARENT_SCOPE) + endif (${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY) +endfunction(get_target_headers TARGET) diff --git a/backend/cmake/ElasticaSetupFlagsTarget.cmake b/backend/cmake/ElasticaSetupFlagsTarget.cmake new file mode 100644 index 000000000..f8b527aab --- /dev/null +++ b/backend/cmake/ElasticaSetupFlagsTarget.cmake @@ -0,0 +1,7 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. + +# The library that contains all the flags for building elastica targets. +# Generator expressions should be used to limit flag scopes to specific +# languages and compilers. +add_library(ElasticaFlags INTERFACE) diff --git a/backend/cmake/EnableWarnings.cmake b/backend/cmake/EnableWarnings.cmake new file mode 100644 index 000000000..426371924 --- /dev/null +++ b/backend/cmake/EnableWarnings.cmake @@ -0,0 +1,66 @@ +# Enable warnings, aim for no warnings + +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +# Distributed under the MIT License. See LICENSE for details. + +include(AddCxxFlag) + +# On systems where we can't use -isystem (Cray), we don't want all the warnings +# enabled because we get flooded with system warnings. +option(ENABLE_WARNINGS "Enable the default warning level" ON) +if (${ENABLE_WARNINGS}) + create_cxx_flags_target( + "-W;\ +-Wall;\ +-Wextra;\ +-Wpedantic;\ +-Wcast-align;\ +-Wcast-qual;\ +-Wdisabled-optimization;\ +-Wformat=2;\ +-Wformat-nonliteral;\ +-Wformat-security;\ +-Wformat-y2k;\ +-Winvalid-pch;\ +-Wmissing-field-initializers;\ +-Wmissing-format-attribute;\ +-Wmissing-include-dirs;\ +-Wmissing-noreturn;\ +-Wno-documentation-unknown-command;\ +-Wno-mismatched-tags;\ +-Wnon-virtual-dtor;\ +-Wold-style-cast;\ +-Woverloaded-virtual;\ +-Wpacked;\ +-Wpointer-arith;\ +-Wredundant-decls;\ +-Wshadow;\ +-Wsign-conversion;\ +-Wstack-protector;\ +-Wswitch-default;\ +-Wunreachable-code;\ +-Wwrite-strings;\ +-Werror=switch" ElasticaWarnings) +endif () +# Disabled these two because of errors +#-Wdocumentation;\ +#-Wnewline-eof;\ + +# GCC 7.1ish and newer warn about noexcept changing mangled names, +# but we don't care +create_cxx_flag_target("-Wno-noexcept-type" ElasticaWarnNoNoexceptType) + +#check_and_add_cxx_link_flag("-Qunused-arguments") + +target_link_libraries( + ElasticaWarnings + INTERFACE + ElasticaWarnNoNoexceptType +) + +target_link_libraries( + ElasticaFlags + INTERFACE + ElasticaWarnings +) diff --git a/backend/cmake/FindBlaze.cmake b/backend/cmake/FindBlaze.cmake new file mode 100644 index 000000000..265a04218 --- /dev/null +++ b/backend/cmake/FindBlaze.cmake @@ -0,0 +1,59 @@ +# Distributed under the MIT License. See LICENSE.txt for details. +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +include(GetEnvPath) +getenv_path(BLAZE_ROOT) + +find_path(BLAZE_INCLUDE_DIR + PATH_SUFFIXES include + NAMES blaze/Blaze.h + HINTS ${BLAZE_ROOT} ${ENV_BLAZE_ROOT} + DOC "Blaze include directory. Used BLAZE_ROOT to set a search dir.") + +set(BLAZE_INCLUDE_DIRS ${BLAZE_INCLUDE_DIR}) +set(BLAZE_VERSION "") + +if (EXISTS "${BLAZE_INCLUDE_DIRS}/blaze/system/Version.h") + # Extract version info from header + file(READ "${BLAZE_INCLUDE_DIRS}/blaze/system/Version.h" + BLAZE_FIND_HEADER_CONTENTS) + + string(REGEX MATCH + "#define BLAZE_MAJOR_VERSION [0-9]+" + BLAZE_MAJOR_VERSION + "${BLAZE_FIND_HEADER_CONTENTS}") + string(REPLACE "#define BLAZE_MAJOR_VERSION " + "" + BLAZE_MAJOR_VERSION + ${BLAZE_MAJOR_VERSION}) + + string(REGEX MATCH + "#define BLAZE_MINOR_VERSION [0-9]+" + BLAZE_MINOR_VERSION + "${BLAZE_FIND_HEADER_CONTENTS}") + string(REPLACE "#define BLAZE_MINOR_VERSION " + "" + BLAZE_MINOR_VERSION + ${BLAZE_MINOR_VERSION}) + + set(BLAZE_VERSION "${BLAZE_MAJOR_VERSION}.${BLAZE_MINOR_VERSION}") +else () + message(WARNING "Failed to find file " + "'${BLAZE_INCLUDE_DIRS}/blaze/system/Version.h' " + "while detecting the Blaze version.") +endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Blaze + FOUND_VAR + BLAZE_FOUND + REQUIRED_VARS + BLAZE_INCLUDE_DIR + BLAZE_INCLUDE_DIRS + VERSION_VAR + BLAZE_VERSION) +mark_as_advanced(BLAZE_INCLUDE_DIR + BLAZE_INCLUDE_DIRS + BLAZE_VERSION + BLAZE_MAJOR_VERSION + BLAZE_MINOR_VERSION) diff --git a/backend/cmake/FindBlazeTensor.cmake b/backend/cmake/FindBlazeTensor.cmake new file mode 100644 index 000000000..22063e66c --- /dev/null +++ b/backend/cmake/FindBlazeTensor.cmake @@ -0,0 +1,61 @@ +# Distributed under the MIT License. See LICENSE.txt for details. +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +include(GetEnvPath) + +getenv_path(BLAZE_TENSOR_ROOT) + +find_path(BLAZE_TENSOR_INCLUDE_DIR + PATH_SUFFIXES include + NAMES blaze_tensor/Blaze.h + HINTS ${BLAZE_TENSOR_ROOT} ${ENV_BLAZE_TENSOR_ROOT} + DOC "Blaze Tensor include directory. Used BLAZE_TENSOR_ROOT to set a search dir.") + +set(BLAZE_TENSOR_INCLUDE_DIRS ${BLAZE_TENSOR_INCLUDE_DIR}) +set(BLAZE_TENSOR_VERSION "") + +if (EXISTS "${BLAZE_TENSOR_INCLUDE_DIRS}/blaze_tensor/system/Version.h") + # Extract version info from header + file(READ "${BLAZE_TENSOR_INCLUDE_DIRS}/blaze_tensor/system/Version.h" + BLAZE_TENSOR_FIND_HEADER_CONTENTS) + + string(REGEX MATCH + "#define BLAZE_TENSOR_MAJOR_VERSION [0-9]+" + BLAZE_TENSOR_MAJOR_VERSION + "${BLAZE_TENSOR_FIND_HEADER_CONTENTS}") + string(REPLACE "#define BLAZE_TENSOR_MAJOR_VERSION " + "" + BLAZE_TENSOR_MAJOR_VERSION + ${BLAZE_TENSOR_MAJOR_VERSION}) + + string(REGEX MATCH + "#define BLAZE_TENSOR_MINOR_VERSION [0-9]+" + BLAZE_TENSOR_MINOR_VERSION + "${BLAZE_TENSOR_FIND_HEADER_CONTENTS}") + string(REPLACE "#define BLAZE_TENSOR_MINOR_VERSION " + "" + BLAZE_TENSOR_MINOR_VERSION + ${BLAZE_TENSOR_MINOR_VERSION}) + + set(BLAZE_TENSOR_VERSION "${BLAZE_TENSOR_MAJOR_VERSION}.${BLAZE_TENSOR_MINOR_VERSION}") + +else () + message(WARNING "Failed to find file " + "'${BLAZE_TENSOR_INCLUDE_DIRS}/blaze_tensor/system/Version.h' " + "while detecting the BlazeTensor version.") +endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(BlazeTensor + FOUND_VAR + BLAZETENSOR_FOUND + REQUIRED_VARS + BLAZE_TENSOR_INCLUDE_DIR + BLAZE_TENSOR_INCLUDE_DIRS + VERSION_VAR + BLAZE_TENSOR_VERSION) +mark_as_advanced(BLAZE_TENSOR_INCLUDE_DIR + BLAZE_TENSOR_INCLUDE_DIRS + BLAZE_TENSOR_VERSION + BLAZE_TENSOR_MAJOR_VERSION + BLAZE_TENSOR_MINOR_VERSION) diff --git a/backend/cmake/FindFilesystem.cmake b/backend/cmake/FindFilesystem.cmake new file mode 100644 index 000000000..dc8f61b78 --- /dev/null +++ b/backend/cmake/FindFilesystem.cmake @@ -0,0 +1,245 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# This is copied from: +# https://github.com/vector-of-bool/CMakeCM/blob/master/modules/FindFilesystem.cmake + +#[=======================================================================[.rst: + +FindFilesystem +############## + +This module supports the C++17 standard library's filesystem utilities. Use the +:imp-target:`std::filesystem` imported target to + +Options +******* + +The ``COMPONENTS`` argument to this module supports the following values: + +.. find-component:: Experimental + :name: fs.Experimental + + Allows the module to find the "experimental" Filesystem TS version of the + Filesystem library. This is the library that should be used with the + ``std::experimental::filesystem`` namespace. + +.. find-component:: Final + :name: fs.Final + + Finds the final C++17 standard version of the filesystem library. + +If no components are provided, behaves as if the +:find-component:`fs.Final` component was specified. + +If both :find-component:`fs.Experimental` and :find-component:`fs.Final` are +provided, first looks for ``Final``, and falls back to ``Experimental`` in case +of failure. If ``Final`` is found, :imp-target:`std::filesystem` and all +:ref:`variables ` will refer to the ``Final`` version. + + +Imported Targets +**************** + +.. imp-target:: std::filesystem + + The ``std::filesystem`` imported target is defined when any requested + version of the C++ filesystem library has been found, whether it is + *Experimental* or *Final*. + + If no version of the filesystem library is available, this target will not + be defined. + + .. note:: + This target has ``cxx_std_17`` as an ``INTERFACE`` + :ref:`compile language standard feature `. Linking + to this target will automatically enable C++17 if no later standard + version is already required on the linking target. + + +.. _fs.variables: + +Variables +********* + +.. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL + + Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++ + filesystem library was found, otherwise ``FALSE``. + +.. variable:: CXX_FILESYSTEM_HAVE_FS + + Set to ``TRUE`` when a filesystem header was found. + +.. variable:: CXX_FILESYSTEM_HEADER + + Set to either ``filesystem`` or ``experimental/filesystem`` depending on + whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was + found. + +.. variable:: CXX_FILESYSTEM_NAMESPACE + + Set to either ``std::filesystem`` or ``std::experimental::filesystem`` + depending on whether :find-component:`fs.Final` or + :find-component:`fs.Experimental` was found. + + +Examples +******** + +Using `find_package(Filesystem)` with no component arguments: + +.. code-block:: cmake + + find_package(Filesystem REQUIRED) + + add_executable(my-program main.cpp) + target_link_libraries(my-program PRIVATE std::filesystem) + + +#]=======================================================================] + + +if(TARGET std::filesystem) + # This module has already been processed. Don't do it again. + return() +endif() + +include(CMakePushCheckState) +include(CheckIncludeFileCXX) +include(CheckCXXSourceCompiles) + +cmake_push_check_state() + +set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY}) + +# All of our tests required C++17 or later +#set(CMAKE_CXX_STANDARD 14) +list(APPEND CMAKE_REQUIRED_FLAGS "-std=c++14") + +# Normalize and check the component list we were given +set(want_components ${Filesystem_FIND_COMPONENTS}) +if(Filesystem_FIND_COMPONENTS STREQUAL "") + set(want_components Final) +endif() + +# Warn on any unrecognized components +set(extra_components ${want_components}) +list(REMOVE_ITEM extra_components Final Experimental) +foreach(component IN LISTS extra_components) + message(WARNING "Extraneous find_package component for Filesystem: ${component}") +endforeach() + +# Detect which of Experimental and Final we should look for +set(find_experimental TRUE) +set(find_final TRUE) +if(NOT "Final" IN_LIST want_components) + set(find_final FALSE) +endif() +if(NOT "Experimental" IN_LIST want_components) + set(find_experimental FALSE) +endif() + +if(find_final) + check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER) + mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER) + if(_CXX_FILESYSTEM_HAVE_HEADER) + # We found the non-experimental header. Don't bother looking for the + # experimental one. + set(find_experimental FALSE) + endif() +else() + set(_CXX_FILESYSTEM_HAVE_HEADER FALSE) +endif() + +if(find_experimental) + check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) + mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) +else() + set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE) +endif() + +if(_CXX_FILESYSTEM_HAVE_HEADER) + set(_have_fs TRUE) + set(_fs_header filesystem) + set(_fs_namespace std::__fs::filesystem) +elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) + set(_have_fs TRUE) + set(_fs_header experimental/filesystem) + set(_fs_namespace std::experimental::filesystem) +else() + set(_have_fs FALSE) +endif() +#message("Filesystem" ${_have_fs}) +#message("Filesystem" ${_fs_header}) + +set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers" FORCE) +set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs" FORCE) +set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs" FORCE) + +message("Filesystem HAVE" ${CXX_FILESYSTEM_HAVE_FS}) +message("Filesystem" ${CXX_FILESYSTEM_HEADER}) +message("Filesystem Namespace" ${CXX_FILESYSTEM_NAMESPACE}) + +set(_found FALSE) + +if(CXX_FILESYSTEM_HAVE_FS) + # We have some filesystem library available. Do link checks + string(CONFIGURE [[ + #include <@CXX_FILESYSTEM_HEADER@> + + int main() { + auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path(); + return static_cast(cwd.string().size()); + } + ]] code @ONLY) + + set(MYSTR "this is my str; having two;") + message(${MYSTR}) + + message(${code}) + # Try to compile a simple filesystem program without any linker flags + check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED) + + set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED}) + + if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED) + set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES}) + # Add the libstdc++ flag + set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs) + message(${code}) + check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED) + set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED}) + message("stdc++" ${CXX_FILESYSTEM_STDCPPFS_NEEDED}) + if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED) + # Try the libc++ flag + set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs) + message(${code}) + check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS_NEEDED) + set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED}) + message("c++gs" ${CXX_FILESYSTEM_CPPFS_NEEDED}) + endif() + endif() + + if(can_link) + add_library(std::filesystem INTERFACE IMPORTED) + target_compile_features(std::filesystem INTERFACE cxx_std_14) + set(_found TRUE) + + if(CXX_FILESYSTEM_NO_LINK_NEEDED) + # Nothing to add... + elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED) + target_link_libraries(std::filesystem INTERFACE -lstdc++fs) + elseif(CXX_FILESYSTEM_CPPFS_NEEDED) + target_link_libraries(std::filesystem INTERFACE -lc++fs) + endif() + endif() +endif() + +cmake_pop_check_state() + +set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can compile and link a program using std::filesystem" FORCE) + +if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND) + message(FATAL_ERROR "Cannot Compile simple program using std::filesystem") +endif() diff --git a/backend/cmake/FindSleef.cmake b/backend/cmake/FindSleef.cmake new file mode 100644 index 000000000..84e32f13a --- /dev/null +++ b/backend/cmake/FindSleef.cmake @@ -0,0 +1,147 @@ +# =============================================== +# Do the final processing for the package find. +# =============================================== +macro(findpkg_finish PREFIX) + # skip if already processed during this run + if (NOT ${PREFIX}_FOUND) + if (${PREFIX}_INCLUDE_DIR AND ${PREFIX}_LIBRARY) + set(${PREFIX}_FOUND TRUE) + set(${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIR}) + set(${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARY}) + else () + if (${PREFIX}_FIND_REQUIRED AND NOT ${PREFIX}_FIND_QUIETLY) + message(FATAL_ERROR "Required library ${PREFIX} not found.") + endif () + endif () + + # mark the following variables as internal variables + mark_as_advanced(${PREFIX}_INCLUDE_DIR + ${PREFIX}_LIBRARY + ${PREFIX}_LIBRARY_DEBUG + ${PREFIX}_LIBRARY_RELEASE) + endif () +endmacro() + +# =============================================== +# See if we have env vars to help us find Sleef +# =============================================== +macro(getenv_path VAR) + set(ENV_${VAR} $ENV{${VAR}}) + # replace won't work if var is blank + if (ENV_${VAR}) + string(REGEX + REPLACE "\\\\" + "/" + ENV_${VAR} + ${ENV_${VAR}}) + endif () +endmacro() + +# ============================================================================= +# Now to actually find Sleef +# + +# Get path, convert backslashes as ${ENV_${var}} +getenv_path(SLEEF_ROOT) + +# initialize search paths +set(SLEEF_PREFIX_PATH ${SLEEF_ROOT} ${ENV_SLEEF_ROOT}) +set(SLEEF_INC_SEARCH_PATH "") +set(SLEEF_LIB_SEARCH_PATH "") + +# If user built from sources +set(SLEEF_BUILD_PREFIX $ENV{SLEEF_BUILD_PREFIX}) +if (SLEEF_BUILD_PREFIX AND ENV_SLEEF_ROOT) + getenv_path(SLEEF_BUILD_DIR) + if (NOT ENV_SLEEF_BUILD_DIR) + set(ENV_SLEEF_BUILD_DIR ${ENV_SLEEF_ROOT}/build) + endif () + + # include directory under ${ENV_SLEEF_ROOT}/include + list(APPEND SLEEF_LIB_SEARCH_PATH + ${ENV_SLEEF_BUILD_DIR}/${SLEEF_BUILD_PREFIX}_release + ${ENV_SLEEF_BUILD_DIR}/${SLEEF_BUILD_PREFIX}_debug) +endif () + +# add general search paths +foreach (dir + IN + LISTS + SLEEF_PREFIX_PATH) + list(APPEND SLEEF_LIB_SEARCH_PATH + ${dir}/lib + ${dir}/Lib + ${dir}/lib/sleef + ${dir}/Libs) + list(APPEND SLEEF_INC_SEARCH_PATH + ${dir}/include + ${dir}/Include + ${dir}/include/sleef) +endforeach () + +log_heavy_debug("SLEEF PREFIX : ${SLEEF_PREFIX_PATH}") +log_heavy_debug("SLEEF_INC : ${SLEEF_INC_SEARCH_PATH}") +log_heavy_debug("SLEEF_LIB : ${SLEEF_LIB_SEARCH_PATH}") + +set(SLEEF_LIBRARY_NAMES sleef) + +find_path(SLEEF_INCLUDE_DIR NAMES sleef.h PATHS ${SLEEF_INC_SEARCH_PATH}) + +log_heavy_debug("SLEEF_INCLUDE_DIR : ${SLEEF_INCLUDE_DIR}") + +find_library(SLEEF_LIBRARY + NAMES ${SLEEF_LIBRARY_NAMES} + PATHS ${SLEEF_LIB_SEARCH_PATH}) + +log_heavy_debug("SLEEF_FIND_LIBRARY : ${SLEEF_LIBRARY}") + +findpkg_finish(SLEEF) + +# if we haven't found Sleef no point on going any further +if (NOT SLEEF_FOUND) + return() +endif () + +# ============================================================================= +# parse all the version numbers from tbb +if (NOT SLEEF_VERSION) + + set(SLEEF_HEADER "${SLEEF_INCLUDE_DIR}/sleef.h") + + # only read the start of the file + file(READ ${SLEEF_HEADER} SLEEF_VERSION_CONTENTS + LIMIT 2048) + + string(REGEX + REPLACE ".*#define SLEEF_VERSION_MAJOR ([0-9]+).*" + "\\1" + SLEEF_VERSION_MAJOR + "${SLEEF_VERSION_CONTENTS}") + + string(REGEX + REPLACE ".*#define SLEEF_VERSION_MINOR ([0-9]+).*" + "\\1" + SLEEF_VERSION_MINOR + "${SLEEF_VERSION_CONTENTS}") + + string(REGEX + REPLACE ".*#define SLEEF_VERSION_PATCHLEVEL ([0-9]+).*" + "\\1" + SLEEF_VERSION_PATCH + "${SLEEF_VERSION_CONTENTS}") + + set(SLEEF_VERSION + "${SLEEF_VERSION_MAJOR}.${SLEEF_VERSION_MINOR}.${SLEEF_VERSION_PATCH}") +endif () + +set(Sleef_VERSION ${SLEEF_VERSION}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + Sleef + FOUND_VAR SLEEF_FOUND + REQUIRED_VARS SLEEF_INCLUDE_DIRS SLEEF_LIBRARIES + VERSION_VAR SLEEF_VERSION) +mark_as_advanced(SLEEF_INCLUDE_DIRS SLEEF_LIBRARIES + SLEEF_VERSION_MAJOR SLEEF_VERSION_MINOR SLEEF_VERSION_PATCH + SLEEF_VERSION Sleef_VERSION) diff --git a/backend/cmake/FindTBB.cmake b/backend/cmake/FindTBB.cmake new file mode 100644 index 000000000..50065acda --- /dev/null +++ b/backend/cmake/FindTBB.cmake @@ -0,0 +1,417 @@ +# * Find ThreadingBuildingBlocks include dirs and libraries Use this module by +# invoking find_package with the form: find_package(TBB [REQUIRED] # Fail with +# error if TBB is not found ) # Once done, this will +# define +# +# TBB_FOUND - system has TBB TBB_INCLUDE_DIRS - the TBB include directories +# TBB_LIBRARIES - TBB libraries to be lined, doesn't include malloc or malloc +# proxy +# +# TBB_VERSION_MAJOR - Major Product Version Number TBB_VERSION_MINOR - Minor +# Product Version Number TBB_INTERFACE_VERSION - Engineering Focused Version +# Number TBB_COMPATIBLE_INTERFACE_VERSION - The oldest major interface version +# still supported. This uses the engineering focused interface version numbers. +# +# TBB_MALLOC_FOUND - system has TBB malloc library TBB_MALLOC_INCLUDE_DIRS - the +# TBB malloc include directories TBB_MALLOC_LIBRARIES - The TBB malloc libraries +# to be lined +# +# TBB_MALLOC_PROXY_FOUND - system has TBB malloc proxy library +# TBB_MALLOC_PROXY_INCLUDE_DIRS = the TBB malloc proxy include directories +# TBB_MALLOC_PROXY_LIBRARIES - The TBB malloc proxy libraries to be lined +# +# This module reads hints about search locations from variables: ENV +# TBB_ARCH_PLATFORM - for eg. set it to "mic" for Xeon Phi builds ENV TBB_ROOT +# or just TBB_ROOT - root directory of tbb installation ENV TBB_BUILD_PREFIX - +# specifies the build prefix for user built tbb libraries. Should be specified +# with ENV TBB_ROOT and optionally... ENV TBB_BUILD_DIR - if build directory is +# different than ${TBB_ROOT}/build +# +# Modified by Robert Maynard from the original OGRE source +# +# ------------------------------------------------------------------- +# This file is part of the CMake build system for OGRE (Object-oriented Graphics +# Rendering Engine) For the latest info, see http://www.ogre3d.org/ +# +# The contents of this file are placed in the public domain. Feel free to make +# use of it in any way you like. +# ------------------------------------------------------------------- +# +# ============================================================================= +# Copyright 2010-2012 Kitware, Inc. Copyright 2012 Rolf Eike Beer +# +# Distributed under the OSI-approved BSD License (the "License"); see +# accompanying file 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 CMake, substitute the full License text +# for the above reference.) + +# ============================================================================= +# FindTBB helper functions and macros +# + +# =============================================== +# Do the final processing for the package find. +# =============================================== +macro(findpkg_finish PREFIX) + # skip if already processed during this run + if(NOT ${PREFIX}_FOUND) + if(${PREFIX}_INCLUDE_DIR AND ${PREFIX}_LIBRARY) + set(${PREFIX}_FOUND TRUE) + set(${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIR}) + set(${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARY}) + else() + if(${PREFIX}_FIND_REQUIRED AND NOT ${PREFIX}_FIND_QUIETLY) + message(FATAL_ERROR "Required library ${PREFIX} not found.") + endif() + endif() + + # mark the following variables as internal variables + mark_as_advanced(${PREFIX}_INCLUDE_DIR + ${PREFIX}_LIBRARY + ${PREFIX}_LIBRARY_DEBUG + ${PREFIX}_LIBRARY_RELEASE) + endif() +endmacro() + +# =============================================== +# Generate debug names from given release names +# =============================================== +macro(get_debug_names PREFIX) + foreach(i ${${PREFIX}}) + set(${PREFIX}_DEBUG + ${${PREFIX}_DEBUG} + ${i}d + ${i}D + ${i}_d + ${i}_D + ${i}_debug + ${i}) + endforeach() +endmacro() + +# =============================================== +# See if we have env vars to help us find tbb +# =============================================== +macro(getenv_path VAR) + set(ENV_${VAR} $ENV{${VAR}}) + # replace won't work if var is blank + if(ENV_${VAR}) + string(REGEX + REPLACE "\\\\" + "/" + ENV_${VAR} + ${ENV_${VAR}}) + endif() +endmacro() + +# =============================================== +# Couple a set of release AND debug libraries +# =============================================== +macro(make_library_set PREFIX) + if(${PREFIX}_RELEASE AND ${PREFIX}_DEBUG) + set(${PREFIX} + optimized + ${${PREFIX}_RELEASE} + debug + ${${PREFIX}_DEBUG}) + elseif(${PREFIX}_RELEASE) + set(${PREFIX} ${${PREFIX}_RELEASE}) + elseif(${PREFIX}_DEBUG) + set(${PREFIX} ${${PREFIX}_DEBUG}) + endif() +endmacro() + +# ============================================================================= +# Now to actually find TBB +# + +# Get path, convert backslashes as ${ENV_${var}} +getenv_path(TBB_ROOT) + +# initialize search paths +set(TBB_PREFIX_PATH ${TBB_ROOT} ${ENV_TBB_ROOT}) +set(TBB_INC_SEARCH_PATH "") +set(TBB_LIB_SEARCH_PATH "") + +# If user built from sources +set(TBB_BUILD_PREFIX $ENV{TBB_BUILD_PREFIX}) +if(TBB_BUILD_PREFIX AND ENV_TBB_ROOT) + getenv_path(TBB_BUILD_DIR) + if(NOT ENV_TBB_BUILD_DIR) + set(ENV_TBB_BUILD_DIR ${ENV_TBB_ROOT}/build) + endif() + + # include directory under ${ENV_TBB_ROOT}/include + list(APPEND TBB_LIB_SEARCH_PATH + ${ENV_TBB_BUILD_DIR}/${TBB_BUILD_PREFIX}_release + ${ENV_TBB_BUILD_DIR}/${TBB_BUILD_PREFIX}_debug) +endif() + +# For Windows, let's assume that the user might be using the precompiled TBB +# packages from the main website. These use a rather awkward directory structure +# (at least for automatically finding the right files) depending on platform and +# compiler, but we'll do our best to accommodate it. Not adding the same effort +# for the precompiled linux builds, though. Those have different versions for CC +# compiler versions and linux kernels which will never adequately match the +# user's setup, so there is no feasible way to detect the "best" version to use. +# The user will have to manually select the right files. (Chances are the +# distributions are shipping their custom version of tbb, anyway, so the problem +# is probably nonexistent.) +if(WIN32 AND MSVC) + set(COMPILER_PREFIX "vc7.1") + if(MSVC_VERSION EQUAL 1400) + set(COMPILER_PREFIX "vc8") + elseif(MSVC_VERSION EQUAL 1500) + set(COMPILER_PREFIX "vc9") + elseif(MSVC_VERSION EQUAL 1600) + set(COMPILER_PREFIX "vc10") + elseif(MSVC_VERSION EQUAL 1700) + set(COMPILER_PREFIX "vc11") + elseif(MSVC_VERSION EQUAL 1800) + set(COMPILER_PREFIX "vc12") + elseif(MSVC_VERSION EQUAL 1900) + set(COMPILER_PREFIX "vc14") + endif() + + # for each prefix path, add ia32/64\${COMPILER_PREFIX}\lib to the lib search + # path + foreach(dir + IN + LISTS + TBB_PREFIX_PATH) + if(CMAKE_CL_64) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia64/${COMPILER_PREFIX}/lib) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia64/${COMPILER_PREFIX}) + list(APPEND TBB_LIB_SEARCH_PATH + ${dir}/intel64/${COMPILER_PREFIX}/lib) + list(APPEND TBB_LIB_SEARCH_PATH + ${dir}/lib/intel64/${COMPILER_PREFIX}) + else() + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia32/${COMPILER_PREFIX}/lib) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia32/${COMPILER_PREFIX}) + endif() + endforeach() +endif() + +# For OS X binary distribution, choose libc++ based libraries for Mavericks +# (10.9) and above and AppleClang +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" + AND NOT CMAKE_SYSTEM_VERSION VERSION_LESS 13.0) + set(USE_LIBCXX OFF) + cmake_policy(GET CMP0025 POLICY_VAR) + + if(POLICY_VAR STREQUAL "NEW") + if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + set(USE_LIBCXX ON) + endif() + else() + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(USE_LIBCXX ON) + endif() + endif() + + if(USE_LIBCXX) + foreach(dir + IN + LISTS + TBB_PREFIX_PATH) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/libc++ ${dir}/libc++/lib) + endforeach() + endif() +endif() + +# check compiler ABI +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(COMPILER_PREFIX) + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) + list(APPEND COMPILER_PREFIX "gcc4.7") + endif() + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4) + list(APPEND COMPILER_PREFIX "gcc4.4") + endif() + list(APPEND COMPILER_PREFIX "gcc4.1") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(COMPILER_PREFIX) + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.6) + list(APPEND COMPILER_PREFIX "gcc4.7") + endif() + list(APPEND COMPILER_PREFIX "gcc4.4") +else() # Assume compatibility with 4.4 for other compilers + list(APPEND COMPILER_PREFIX "gcc4.4") +endif() + +# if platform architecture is explicitly specified +set(TBB_ARCH_PLATFORM $ENV{TBB_ARCH_PLATFORM}) +if(TBB_ARCH_PLATFORM) + foreach(dir + IN + LISTS + TBB_PREFIX_PATH) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/${TBB_ARCH_PLATFORM}/lib) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/${TBB_ARCH_PLATFORM}) + endforeach() +endif() + +foreach(dir + IN + LISTS + TBB_PREFIX_PATH) + foreach(prefix + IN + LISTS + COMPILER_PREFIX) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/intel64) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/intel64/${prefix}) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/intel64/lib) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/intel64/${prefix}/lib) + else() + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia32) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia32/${prefix}) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia32/lib) + list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia32/${prefix}/lib) + endif() + endforeach() +endforeach() + +# add general search paths +foreach(dir + IN + LISTS + TBB_PREFIX_PATH) + list(APPEND TBB_LIB_SEARCH_PATH + ${dir}/lib + ${dir}/Lib + ${dir}/lib/tbb + ${dir}/Libs) + list(APPEND TBB_INC_SEARCH_PATH + ${dir}/include + ${dir}/Include + ${dir}/include/tbb) +endforeach() + +set(TBB_LIBRARY_NAMES tbb) +get_debug_names(TBB_LIBRARY_NAMES) + +find_path(TBB_INCLUDE_DIR NAMES tbb/tbb.h PATHS ${TBB_INC_SEARCH_PATH}) + +find_library(TBB_LIBRARY_RELEASE + NAMES ${TBB_LIBRARY_NAMES} + PATHS ${TBB_LIB_SEARCH_PATH}) +# Need not find debug libraries to pass on to INTERFACE_LINK_LIBRARIES +#find_library(TBB_LIBRARY_DEBUG +# NAMES ${TBB_LIBRARY_NAMES_DEBUG} +# PATHS ${TBB_LIB_SEARCH_PATH}) +make_library_set(TBB_LIBRARY) + +findpkg_finish(TBB) + +# if we haven't found TBB no point on going any further +if(NOT TBB_FOUND) + return() +endif() + +# ============================================================================= +# Look for TBB's malloc package +set(TBB_MALLOC_LIBRARY_NAMES tbbmalloc) +get_debug_names(TBB_MALLOC_LIBRARY_NAMES) + +find_path(TBB_MALLOC_INCLUDE_DIR NAMES tbb/tbb.h PATHS ${TBB_INC_SEARCH_PATH}) + +find_library(TBB_MALLOC_LIBRARY_RELEASE + NAMES ${TBB_MALLOC_LIBRARY_NAMES} + PATHS ${TBB_LIB_SEARCH_PATH}) +# Need not find debug libraries to pass on to INTERFACE_LINK_LIBRARIES +#find_library(TBB_MALLOC_LIBRARY_DEBUG +# NAMES ${TBB_MALLOC_LIBRARY_NAMES_DEBUG} +# PATHS ${TBB_LIB_SEARCH_PATH}) +make_library_set(TBB_MALLOC_LIBRARY) + +findpkg_finish(TBB_MALLOC) + +# ============================================================================= +# Look for TBB's malloc proxy package +set(TBB_MALLOC_PROXY_LIBRARY_NAMES tbbmalloc_proxy) +get_debug_names(TBB_MALLOC_PROXY_LIBRARY_NAMES) + +find_path(TBB_MALLOC_PROXY_INCLUDE_DIR + NAMES tbb/tbbmalloc_proxy.h + PATHS ${TBB_INC_SEARCH_PATH}) + +find_library(TBB_MALLOC_PROXY_LIBRARY_RELEASE + NAMES ${TBB_MALLOC_PROXY_LIBRARY_NAMES} + PATHS ${TBB_LIB_SEARCH_PATH}) +# Need not find debug libraries to pass on to INTERFACE_LINK_LIBRARIES +#find_library(TBB_MALLOC_PROXY_LIBRARY_DEBUG +# NAMES ${TBB_MALLOC_PROXY_LIBRARY_NAMES_DEBUG} +# PATHS ${TBB_LIB_SEARCH_PATH}) +make_library_set(TBB_MALLOC_PROXY_LIBRARY) + +findpkg_finish(TBB_MALLOC_PROXY) + +# ============================================================================= +# parse all the version numbers from tbb +if(NOT TBB_VERSION) + + set(TBB_STDDEF_FILE "${TBB_INCLUDE_DIR}/tbb/tbb_stddef.h") + + if (EXISTS ${TBB_STDDEF_FILE}) + # For earlier versions, the filename is tbb_stddef.h + set(TBB_VERSIONS_FILE ${TBB_STDDEF_FILE}) + else() + # For later versions, the filename is version.h + set(TBB_VERSIONS_FILE "${TBB_INCLUDE_DIR}/oneapi/tbb/version.h") + endif() + + # only read the start of the file + file(READ ${TBB_VERSIONS_FILE} TBB_VERSION_CONTENTS + LIMIT 2048) + + string(REGEX + REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" + "\\1" + TBB_VERSION_MAJOR + "${TBB_VERSION_CONTENTS}") + + string(REGEX + REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" + "\\1" + TBB_VERSION_MINOR + "${TBB_VERSION_CONTENTS}") + + ## Don't rely on the patch version, as old tbb libraries don't provide one + # string(REGEX + # REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" + # "\\1" + # TBB_INTERFACE_VERSION + # "${TBB_VERSION_CONTENTS}") + # math(EXPR TBB_VERSION_MAJOR "${TBB_INTERFACE_VERSION}/1000") + # math(EXPR TBB_VERSION_MINOR "(${TBB_INTERFACE_VERSION}%1000)/10") + + set(TBB_VERSION + ${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}) + +endif() + + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(TBB + FOUND_VAR + TBB_FOUND + REQUIRED_VARS + TBB_INCLUDE_DIR + TBB_INCLUDE_DIRS + VERSION_VAR + TBB_VERSION) +mark_as_advanced(TBB_INCLUDE_DIR + TBB_INCLUDE_DIRS + TBB_MALLOC_INCLUDE_DIRS + TBB_VERSION + TBB_VERSION_MAJOR + TBB_VERSION_MINOR) diff --git a/backend/cmake/FindYamlCpp.cmake b/backend/cmake/FindYamlCpp.cmake new file mode 100644 index 000000000..d6c591705 --- /dev/null +++ b/backend/cmake/FindYamlCpp.cmake @@ -0,0 +1,42 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. + +# Find yaml-cpp: https://github.com/jbeder/yaml-cpp +# If not in one of the default paths specify -D YAMLCPP_ROOT=/path/to/yaml-cpp +# to search there as well. +# Static libraries can be use by setting -D YAMLCPP_STATIC_LIBRARY=ON + +include(CheckCXXSourceRuns) +include(GetEnvPath) + +# Get path, convert backslashes as ${ENV_${var}} +getenv_path(YamlCpp_ROOT) + +if (NOT YamlCpp_ROOT) + # Need to set to empty to avoid warnings with --warn-uninitialized + set(YamlCpp_ROOT "") +endif () + +# find the yaml-cpp include directory +find_path(YamlCpp_INCLUDE_DIRS + PATH_SUFFIXES include + NAMES yaml-cpp/yaml.h + HINTS ${YamlCpp_ROOT} ${ENV_YamlCpp_ROOT} + DOC "YamlCpp include directory. Use YamlCpp_ROOT to set a search dir.") + +if (YamlCpp_STATIC_LIBRARY) + set(YamlCpp_STATIC libyaml-cpp.a) +else (YamlCpp_STATIC_LIBRARY) + # Silence CMake uninitialized variable warning + set(YamlCpp_STATIC "") +endif () + +find_library(YamlCpp_LIBRARIES + PATH_SUFFIXES lib64 lib build + NAMES ${YamlCpp_STATIC} yaml-cpp + HINTS ${YamlCpp_ROOT} ${ENV_YamlCpp_ROOT}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(YamlCpp + DEFAULT_MSG YamlCpp_INCLUDE_DIRS YamlCpp_LIBRARIES) +mark_as_advanced(YamlCpp_INCLUDE_DIRS YamlCpp_LIBRARIES) diff --git a/backend/cmake/GetEnvPath.cmake b/backend/cmake/GetEnvPath.cmake new file mode 100644 index 000000000..3855cedf1 --- /dev/null +++ b/backend/cmake/GetEnvPath.cmake @@ -0,0 +1,15 @@ +# ================================================ +# See if we have env vars to help us find packages +# ================================================ +# Gets path, convert backslashes as ${ENV_${var}} +macro(getenv_path VAR) + set(ENV_${VAR} $ENV{${VAR}}) + # replace won't work if var is blank + if(ENV_${VAR}) + string(REGEX + REPLACE "\\\\" + "/" + ENV_${VAR} + ${ENV_${VAR}}) + endif() +endmacro() diff --git a/backend/cmake/Logging.cmake b/backend/cmake/Logging.cmake new file mode 100644 index 000000000..0fec09f30 --- /dev/null +++ b/backend/cmake/Logging.cmake @@ -0,0 +1,100 @@ +# Utilities for writing cmake log messages. + +# Distributed under the MIT License. See LICENSE for details. + +# Global variables +set(error_log_level "1" CACHE INTERNAL "Error log level") +set(warning_log_level "2" CACHE INTERNAL "Warning log level") +set(info_log_level "3" CACHE INTERNAL "Info log level") +set(debug_log_level "4" CACHE INTERNAL "Debug log level") +set(heavy_debug_log_level "5" CACHE INTERNAL "Heavy_debug log level") + +set(current_log_level "${info_log_level}" CACHE INTERNAL "Current log level") + +function(log_message message) + if(LOG_MESSAGE_STATUS) + set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") + message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") + endif() +endfunction(log_message) + +function(set_logs_on) + set(LOG_MESSAGE_STATUS "ON" CACHE INTERNAL "Status for log messages") +endfunction(set_logs_on) + +function(set_logs_off) + set(LOG_MESSAGE_STATUS "OFF" CACHE INTERNAL "Status for log messages") +endfunction(set_logs_off) + +function(set_log_level log_level) + if(${log_level} MATCHES "^[0-9]+$") + if((${log_level} EQUAL ${error_log_level}) + OR (${log_level} GREATER ${error_log_level})) + set(current_log_level + "${log_level}" + CACHE INTERNAL "Current log level") + endif() + else() + if(${log_level} STREQUAL "ERROR") + set(local_log_level "${error_log_level}") + elseif(${log_level} STREQUAL "WARNING") + set(local_log_level "${warning_log_level}") + elseif(${log_level} STREQUAL "INFO") + set(local_log_level "${info_log_level}") + elseif(${log_level} STREQUAL "DEBUG") + set(local_log_level "${debug_log_level}") + elseif(${log_level} STREQUAL "HEAVYDEBUG") + set(local_log_level "${heavy_debug_log_level}") + endif() + set(current_log_level + "${local_log_level}" + CACHE INTERNAL "Current log level") + endif() +endfunction(set_log_level) + +# set_log_level( "${info_log_level}" ) + +function(log_error message) + set(message_log_level "${error_log_level}") + if((${current_log_level} EQUAL ${message_log_level}) + OR (${current_log_level} GREATER ${message_log_level})) + set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") + message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") + endif() +endfunction(log_error) + +function(log_warning message) + set(message_log_level "${warning_log_level}") + if((${current_log_level} EQUAL ${message_log_level}) + OR (${current_log_level} GREATER ${message_log_level})) + set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") + message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") + endif() +endfunction(log_warning) + +function(log_info message) + set(message_log_level "${info_log_level}") + if((${current_log_level} EQUAL ${message_log_level}) + OR (${current_log_level} GREATER ${message_log_level})) + set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") + message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") + endif() +endfunction(log_info) + +function(log_debug message) + set(message_log_level "${debug_log_level}") + if((${current_log_level} EQUAL ${message_log_level}) + OR (${current_log_level} GREATER ${message_log_level})) + set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") + message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") + endif() +endfunction(log_debug) + +function(log_heavy_debug message) + set(message_log_level "${heavy_debug_log_level}") + if((${current_log_level} EQUAL ${message_log_level}) + OR (${current_log_level} GREATER ${message_log_level})) + set(LOG_MESSAGE_PREFIX "[${PROJECT_NAME}]") + message(STATUS "${LOG_MESSAGE_PREFIX} ${message}") + endif() +endfunction(log_heavy_debug) diff --git a/backend/cmake/PrintUsefulCMakeInfo.cmake b/backend/cmake/PrintUsefulCMakeInfo.cmake new file mode 100644 index 000000000..db2163d2e --- /dev/null +++ b/backend/cmake/PrintUsefulCMakeInfo.cmake @@ -0,0 +1,63 @@ +# Prints information for ease in backtracking + +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +# Distributed under the MIT License. See LICENSE for details. + +log_info("Project Information") +log_info("-------------------") +log_info("-> PROJECT_NAME: ${PROJECT_NAME}") +log_info("-> PROJECT_VERSION: ${PROJECT_VERSION}") +log_info("-> PROJECT_VERSION_MAJOR: ${PROJECT_VERSION_MAJOR}") +log_info("-> PROJECT_VERSION_MINOR: ${PROJECT_VERSION_MINOR}") +log_info("-> PROJECT_VERSION_PATCH: ${PROJECT_VERSION_PATCH}") +log_info("-> GIT_BRANCH: ${GIT_BRANCH}") +log_info("-> GIT_HASH: ${GIT_HASH}") +log_info("-> GIT_DESCRIPTION: ${GIT_DESCRIPTION}") + +log_info("Build Information") +log_info("-----------------") +log_info("-> BUILD_EXAMPLES ${ELASTICA_BUILD_EXAMPLES}") +log_info("-> BUILD_BENCHMARKS ${ELASTICA_BUILD_BENCHMARKS}") +log_info("-> BUILD_TESTS ${ELASTICA_BUILD_TESTS}") +log_info("-> BUILD_DOCS ${ELASTICA_BUILD_DOCUMENTATION}") +log_info("-> BUILD_PYTHON ${ELASTICA_BUILD_PYTHON_BINDINGS}") +log_info("-> Build Directory: ${CMAKE_BINARY_DIR}") +log_info("-> Source Directory: ${CMAKE_SOURCE_DIR}") +log_info("-> Bin Directory: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") +log_info("-> CMake Modules Path: ${CMAKE_MODULE_PATH}") +log_info("-> Operating System: ${CMAKE_SYSTEM_NAME}") + +log_info("Compiler Information") +log_info("--------------------") +log_info("-> CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") +log_info("-> CMAKE_CXX_LINK_FLAGS: ${CMAKE_CXX_LINK_FLAGS}") +log_info("-> CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}") +log_info("-> CMAKE_CXX_FLAGS_RELEASE:${CMAKE_CXX_FLAGS_RELEASE}") +log_info("-> CMAKE_EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}") +log_info("-> CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") +get_property(ETP GLOBAL PROPERTY ELASTICA_THIRD_PARTY_LIBS) +list(JOIN ETP " " ETP) +log_info("-> ELASTICA_LIBRARIES: ${ETP}") +unset(ETP) +log_info("-> USE_SYSTEM_INCLUDE: ${USE_SYSTEM_INCLUDE}") +# message(STATUS "USE_PCH: ${USE_PCH}") + +# if (PYTHONINTERP_FOUND) message(STATUS "Python: " ${PYTHON_EXECUTABLE}) +# message(STATUS "Python Version: ${PYTHON_VERSION_STRING}") else() +# message(STATUS "Python: Not found") endif() + +# if(CLANG_TIDY_BIN) message(STATUS "Found clang-tidy: ${CLANG_TIDY_BIN}") +# elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") message( STATUS "Could not find +# clang-tidy even though LLVM clang is installed" ) endif() + +# if (CODE_COVERAGE) message(STATUS "Code coverage enabled. All prerequisites +# found:") message(STATUS " gcov: ${GCOV}") message(STATUS " lcov: ${LCOV}") +# message(STATUS " genhtml: ${GENHTML}") message(STATUS " sed: ${SED}") +# endif() + +if (DOXYGEN_FOUND) + log_info("Doxygen: " ${DOXYGEN_EXECUTABLE}) +else () + log_info("Doxygen: Not found, documentation cannot be built.") +endif () diff --git a/backend/cmake/SetBuildType.cmake b/backend/cmake/SetBuildType.cmake new file mode 100644 index 000000000..223cfb26c --- /dev/null +++ b/backend/cmake/SetBuildType.cmake @@ -0,0 +1,76 @@ +# Set type of build +# Distributed under the MIT License. See LICENSE for details. +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +# CMake lets the user define CMAKE_BUILD_TYPE on the command line and recognizes +# the values "Debug", "Release", "RelWithDebInfo", and "MinSizeRel", which add +# specific compiler or linker flags. CMake's default behavior is to add no +# additional compiler or linker flags if the user does not define +# CMAKE_BUILD_TYPE on the command line, or passes an unrecognized value. +# We add a sanity check that checks if CMAKE_BUILD_TYPE is one of the recognized +# values, and we also set the CMAKE_BUILD_TYPE to "Debug" if the user does not +# specify it on the command line. In addition, we add "None" as a valid value +# for CMAKE_BUILD_TYPE whose behavior is to add no additional compiler or linker +# flags. This is done by defining the following flags analagous to those used by +# the other build types. Additional build types can be defined in a similar +# manner by defining the appropriate flags, and adding the name of the build +# type to CMAKE_BUILD_TYPES below. +set(CMAKE_CXX_FLAGS_NONE "" CACHE STRING + "Additional flags used by the compiler for Build type None." + FORCE) + +set(CMAKE_C_FLAGS_NONE "" CACHE STRING + "Additional flags used by the compiler for Build type None." + FORCE) + +set(CMAKE_EXE_LINKER_FLAGS_NONE "" CACHE STRING + "Additional flags used by the linker for Build type None." + FORCE) + +set(CMAKE_MODULE_LINKER_FLAGS_NONE "" CACHE STRING + "Additional flags used by the linker for Build type None." + FORCE) + +set(CMAKE_SHARED_LINKER_FLAGS_NONE "" CACHE STRING + "Additional flags used by the linker for Build type None." + FORCE) + +set(CMAKE_STATIC_LINKER_FLAGS_NONE "" CACHE STRING + "Additional flags used by the linker for Build type None." + FORCE) + +mark_as_advanced( + CMAKE_CXX_FLAGS_NONE + CMAKE_C_FLAGS_NONE + CMAKE_EXE_LINKER_FLAGS_NONE + CMAKE_MODULE_LINKER_FLAGS_NONE + CMAKE_SHARED_LINKER_FLAGS_NONE + CMAKE_STATIC_LINKER_FLAGS_NONE +) + + +set(CMAKE_BUILD_TYPES + "Debug" + "Release" + "None" + "RelWithDebInfo" + "MinSizeRel") + +if (NOT CMAKE_BUILD_TYPE) + log_info("-> CMAKE_BUILD_TYPE not specified, setting to 'Debug'") + set(CMAKE_BUILD_TYPE + Debug + CACHE STRING "Choose the type of build: ${CMAKE_BUILD_TYPES}" FORCE) +else () + if (NOT ${CMAKE_BUILD_TYPE} IN_LIST CMAKE_BUILD_TYPES) + message( + FATAL_ERROR + "\n" "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\n" + "Valid values: ${CMAKE_BUILD_TYPES}\n") + endif () + # message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") +endif () + +# cmake-format: off +log_info("CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}") +# cmake-format: on diff --git a/backend/cmake/SetCxxStandard.cmake b/backend/cmake/SetCxxStandard.cmake new file mode 100644 index 000000000..f80e7e3fb --- /dev/null +++ b/backend/cmake/SetCxxStandard.cmake @@ -0,0 +1,7 @@ +# Sets standard, C++14 for now + +# Distributed under the MIT License. See LICENSE for details. + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/backend/cmake/SetOutputDirectory.cmake b/backend/cmake/SetOutputDirectory.cmake new file mode 100644 index 000000000..d5ef81a13 --- /dev/null +++ b/backend/cmake/SetOutputDirectory.cmake @@ -0,0 +1,27 @@ +# Set the directory where the library and executables are placed The default can +# be overridden by specifying `-D CMAKE_RUNTIME_OUTPUT_DIRECTORY=/path/` + +# Distributed under the MIT License. See LICENSE for details. +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/bin/" + CACHE STRING "Choose the directory where executables are placed" FORCE) +endif (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) + +if (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set( + CMAKE_LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/" + CACHE STRING "Choose the directory where shared libraries are placed" FORCE + ) +endif (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) + +if (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set( + CMAKE_ARCHIVE_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/" + CACHE STRING "Choose the directory where static libraries are placed" FORCE + ) +endif (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) diff --git a/backend/cmake/SetupBlaze.cmake b/backend/cmake/SetupBlaze.cmake new file mode 100644 index 000000000..85091a856 --- /dev/null +++ b/backend/cmake/SetupBlaze.cmake @@ -0,0 +1,85 @@ +# Distributed under the MIT License. See LICENSE.txt for details. +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +option(USE_SLEEF "Use Sleef to add more vectorized instructions." OFF) + +log_info("Finding Blaze") + +find_package(Blaze 3.9 REQUIRED QUIET) + +log_debug("BLAZE_INCLUDE_DIR: ${BLAZE_INCLUDE_DIR}") +log_debug("BLAZE_VERSION: ${BLAZE_VERSION}") + +file(APPEND "${CMAKE_BINARY_DIR}/BuildInformation.txt" + "Blaze Version: ${BLAZE_VERSION}\n") + +#elastica_include_directories("${BLAZE_INCLUDE_DIR}") +add_library(Blaze INTERFACE IMPORTED) +set_property(TARGET Blaze PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${BLAZE_INCLUDE_DIR}) + +set(_BLAZE_USE_SLEEF 0) + +if (USE_SLEEF) + # Try to find Sleef to increase vectorization + include(SetupSleef) +endif () + +if (SLEEF_FOUND) + target_link_libraries( + Blaze + INTERFACE + Sleef + ) + set(_BLAZE_USE_SLEEF 1) +endif () + +# Configure Blaze. Some of the Blaze configuration options could be optimized +# for the machine we are running on. See documentation: +# https://bitbucket.org/blaze-lib/blaze/wiki/Configuration%20and%20Installation#!step-2-configuration +target_compile_definitions(Blaze + INTERFACE + # - Enable external BLAS kernels + BLAZE_BLAS_MODE=0 + # - Set default matrix storage order to column-major, since many of our + # functions are implemented for column-major layout. This default reduces + # conversions. + BLAZE_DEFAULT_STORAGE_ORDER=blaze::rowMajor + # - Disable SMP parallelization. This disables SMP parallelization for all + # possible backends (OpenMP, C++11 threads, Boost, HPX): + # https://bitbucket.org/blaze-lib/blaze/wiki/Serial%20Execution#!option-3-deactivation-of-parallel-execution + BLAZE_USE_SHARED_MEMORY_PARALLELIZATION=0 + # - Disable MPI parallelization + BLAZE_MPI_PARALLEL_MODE=0 + # - Using the default cache size, which may have been configured automatically + # by the Blaze CMake configuration for the machine we are running on. We + # could override it here explicitly to tune performance. + # BLAZE_CACHE_SIZE + BLAZE_USE_PADDING=1 + # - Always enable non-temporal stores for cache optimization of large data + # structures: https://bitbucket.org/blaze-lib/blaze/wiki/Configuration%20Files#!streaming-non-temporal-stores + BLAZE_USE_STREAMING=1 + # - Initializing default-constructed structures for fundamental types + BLAZE_USE_DEFAULT_INITIALIZATON=1 + # Use Sleef for vectorization of more math functions + BLAZE_USE_SLEEF=${_BLAZE_USE_SLEEF} + # Set inlining settings + BLAZE_USE_STRONG_INLINE=1 + BLAZE_USE_ALWAYS_INLINE=1 + # Set vectorization (leave to 1, else there is no use for blaze) + BLAZE_USE_VECTORIZATION=1 + ) + +add_interface_lib_headers( + TARGET Blaze + HEADERS + blaze/math/DynamicMatrix.h + blaze/math/DynamicVector.h + blaze/math/StaticVector.h + blaze/system/Version.h +) + +set_property( + GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS + Blaze +) diff --git a/backend/cmake/SetupBlazeTensor.cmake b/backend/cmake/SetupBlazeTensor.cmake new file mode 100644 index 000000000..673ea083b --- /dev/null +++ b/backend/cmake/SetupBlazeTensor.cmake @@ -0,0 +1,34 @@ +# Distributed under the MIT License. See LICENSE.txt for details. +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +log_info("Finding Blaze Tensor") + +find_package(BlazeTensor 0.1 REQUIRED QUIET) + +log_debug("BLAZE_TENSOR_INCLUDE_DIR: ${BLAZE_TENSOR_INCLUDE_DIR}") +log_debug("BLAZE_TENSOR_VERSION: ${BLAZE_TENSOR_VERSION}") + +file(APPEND "${CMAKE_BINARY_DIR}/BuildInformation.txt" + "BlazeTensor Version: ${BLAZE_TENSOR_VERSION}\n") + +#elastica_include_directories("${BLAZE_INCLUDE_DIR}") +add_library(BlazeTensor INTERFACE IMPORTED) +set_property(TARGET BlazeTensor PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${BLAZE_TENSOR_INCLUDE_DIR}) + +add_interface_lib_headers( + TARGET BlazeTensor + HEADERS + blaze_tensor/Blaze.h + blaze_tensor/math/DynamicTensor.h + blaze_tensor/math/StaticTensor.h + blaze_tensor/math/SubTensor.h + blaze_tensor/math/views/ColumnSlice.h + # for the version number in CMakelists + blaze_tensor/system/Version.h +) + +set_property( + GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS + BlazeTensor +) diff --git a/backend/cmake/SetupCxxFlags.cmake b/backend/cmake/SetupCxxFlags.cmake new file mode 100644 index 000000000..67989c0d3 --- /dev/null +++ b/backend/cmake/SetupCxxFlags.cmake @@ -0,0 +1,57 @@ +# Setup all relevant compiler flags + +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +# Distributed under the MIT License. See LICENSE for details. + +option(DEBUG_SYMBOLS "Add -g to CMAKE_CXX_FLAGS if ON, -g0 if OFF." ON) + +option(OVERRIDE_ARCH "The architecture to use. Default is native." OFF) + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DELASTICA_DEBUG") + +if (NOT ${DEBUG_SYMBOLS}) + string(REPLACE "-g " + "-g0 " + CMAKE_CXX_FLAGS_DEBUG + ${CMAKE_CXX_FLAGS_DEBUG}) +endif (NOT ${DEBUG_SYMBOLS}) + +# Always build with -g so we can view backtraces, etc. when production code +# fails. This can be overridden by passing `-D DEBUG_SYMBOLS=OFF` to CMake +if (${DEBUG_SYMBOLS}) + set_property(TARGET ElasticaFlags + APPEND PROPERTY INTERFACE_COMPILE_OPTIONS -g) +endif (${DEBUG_SYMBOLS}) + +# Always compile only for the current architecture. This can be overridden +# by passing `-D OVERRIDE_ARCH=THE_ARCHITECTURE` to CMake +if (NOT "${OVERRIDE_ARCH}" STREQUAL "OFF") + set_property(TARGET ElasticaFlags + APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS + $<$:-march=${OVERRIDE_ARCH}>) +else () + # Apple silicon does not support the march native flag in all compilers. + if(NOT APPLE OR NOT "${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") + set_property(TARGET ElasticaFlags + APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS + $<$:-march=native>) + endif() +endif () + +# We always want a detailed backtrace of template errors to make debugging them +# easier +set_property(TARGET ElasticaFlags + APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS + $<$:-ftemplate-backtrace-limit=0>) + +# By default, the LLVM optimizer assumes floating point exceptions are ignored. +create_cxx_flag_target("-ffp-exception-behavior=maytrap" ElasticaFpExceptions) +target_link_libraries( + ElasticaFlags + INTERFACE + ElasticaFpExceptions +) diff --git a/backend/cmake/SetupElasticaInlining.cmake b/backend/cmake/SetupElasticaInlining.cmake new file mode 100644 index 000000000..e9a04364f --- /dev/null +++ b/backend/cmake/SetupElasticaInlining.cmake @@ -0,0 +1,18 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +option(ELASTICA_USE_ALWAYS_INLINE "Force elastica inlining." ON) + +set(_ELASTICA_USE_ALWAYS_INLINE 0) + +if (ELASTICA_USE_ALWAYS_INLINE) + set(_ELASTICA_USE_ALWAYS_INLINE 1) +endif () + +set_property( + TARGET ElasticaFlags + APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS + $<$:ELASTICA_USE_ALWAYS_INLINE=${_ELASTICA_USE_ALWAYS_INLINE}> +) diff --git a/backend/cmake/SetupFilesystem.cmake b/backend/cmake/SetupFilesystem.cmake new file mode 100644 index 000000000..bbb7d60e6 --- /dev/null +++ b/backend/cmake/SetupFilesystem.cmake @@ -0,0 +1,27 @@ +log_info("Finding Filesystem") + +# Older versions of GCC only supports experimental in some laptops +# So including it here + +#if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") +# find_package(Filesystem REQUIRED COMPONENTS Experimental) +## set(CXX_FILESYSTEM_LIBRARIES "stdc++fs") +## target_link_libraries(elastica PUBLIC ${CXX_FILESYSTEM_LIBRARIES}) +#elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") +# find_package(Filesystem REQUIRED COMPONENTS Final) +## set(CXX_FILESYSTEM_LIBRARIES "") +## set(CXX_FILESYSTEM_LIBRARIES "c++experimental") +#endif () + +# Hacky, ok for now +add_library(std::filesystem INTERFACE IMPORTED) +target_compile_features(std::filesystem INTERFACE cxx_std_14) +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + target_link_libraries(std::filesystem INTERFACE -lstdc++fs) +elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + # target_link_libraries(std::filesystem INTERFACE -lc++fs) +endif () + +#find_package(Filesystem REQUIRED COMPONENTS Final Experimental) + +log_debug("FILESYSTEM_TYPE: ${CXX_FILESYSTEM_HEADER}") diff --git a/backend/cmake/SetupHDF5.cmake b/backend/cmake/SetupHDF5.cmake new file mode 100644 index 000000000..a652b6d95 --- /dev/null +++ b/backend/cmake/SetupHDF5.cmake @@ -0,0 +1,43 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. + +log_info("Finding HDF5") + +find_package(HDF5 COMPONENTS C) + +log_debug("HDF5 library: ${HDF5_C_LIBRARIES}") +log_debug("HDF5 include: ${HDF5_C_INCLUDE_DIRS}") +log_debug("HDF5 version: ${HDF5_VERSION}") + +file(APPEND + "${CMAKE_BINARY_DIR}/BuildInformation.txt" + "HDF5 version: ${HDF5_VERSION}\n" + ) + +if (NOT TARGET hdf5::hdf5) + add_library(hdf5::hdf5 INTERFACE IMPORTED) + set_target_properties( + hdf5::hdf5 + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${HDF5_C_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${HDF5_C_LIBRARIES}" + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${HDF5_C_INCLUDE_DIRS}" + ) + if (DEFINED HDF5_C_DEFINITIONS) + set_target_properties( + hdf5::hdf5 + PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "${HDF5_C_DEFINITIONS}" + ) + endif () +endif () + +if (HDF5_IS_PARALLEL) + message(WARNING "HDF5 is built with MPI support, but MPI was not found. " + "You may encounter build issues with HDF5, such as missing headers.") +endif () + +set_property( + GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS + hdf5::hdf5 +) diff --git a/backend/cmake/SetupMacOsx.cmake b/backend/cmake/SetupMacOsx.cmake new file mode 100644 index 000000000..7a5d93dae --- /dev/null +++ b/backend/cmake/SetupMacOsx.cmake @@ -0,0 +1,19 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. +# Obtained with thanks from https://github.com/sxs-collaboration/spectre + +# https://stackoverflow.com/a/19819591 +if (APPLE) + # The -fvisibility=hidden flag is necessary to eliminate warnings + # when building on Apple Silicon + if("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") + endif() + set(ELASTICA_MACOSX_MIN "10.9") + if (DEFINED MACOSX_MIN) + set(ELASTICA_MACOSX_MIN "${MACOSX_MIN}") + endif () + set(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} -mmacosx-version-min=${ELASTICA_MACOSX_MIN}") + log_info("Minimum macOS version: ${ELASTICA_MACOSX_MIN}") +endif () diff --git a/backend/cmake/SetupPic.cmake b/backend/cmake/SetupPic.cmake new file mode 100644 index 000000000..c5d2d0b3b --- /dev/null +++ b/backend/cmake/SetupPic.cmake @@ -0,0 +1,16 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. + +# Set up position independent code by default since this is required +# for our python libraries. +# +# Obtained with thanks from https://github.com/sxs-collaboration/spectre +# +# Using CMake's set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# enables -fPIC in shared libraries and -fPIE on executables. Here instead +# of a global property, we add it to the ElasticaFlags property which +# is used by our examples, and python builds internally. +set_property(TARGET ElasticaFlags + APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS + $<$:-fPIC>) diff --git a/backend/cmake/SetupPybind11.cmake b/backend/cmake/SetupPybind11.cmake new file mode 100644 index 000000000..393768ef9 --- /dev/null +++ b/backend/cmake/SetupPybind11.cmake @@ -0,0 +1,34 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. +log_info("Finding Pybind11") + +# Make sure to find Python first so it's consistent with pybind11 +find_package(Python COMPONENTS Interpreter Development) + +# Try to find the pybind11-config tool to find pybind11's CMake config files +find_program(PYBIND11_CONFIG_TOOL pybind11-config) +set(PYBIND11_CMAKEDIR "") +if (PYBIND11_CONFIG_TOOL) + execute_process( + COMMAND "${PYBIND11_CONFIG_TOOL}" "--cmakedir" + OUTPUT_VARIABLE PYBIND11_CMAKEDIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Found pybind11-config tool (${PYBIND11_CONFIG_TOOL}) and " + "determined CMake dir: ${PYBIND11_CMAKEDIR}") +endif () + +find_package(pybind11 2.6...<3 REQUIRED + HINTS + ${PYBIND11_CMAKEDIR} + ${Python_SITEARCH} + ${Python_SITELIB} + ${Python_STDARCH} + ${Python_STDLIB} + ) + +set_property( + GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS + pybind11 +) + +log_debug("Pybind11 include: ${pybind11_INCLUDE_DIR}") diff --git a/backend/cmake/SetupSanitizers.cmake b/backend/cmake/SetupSanitizers.cmake new file mode 100644 index 000000000..97ddf81ec --- /dev/null +++ b/backend/cmake/SetupSanitizers.cmake @@ -0,0 +1,72 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. + +option(ASAN "Add AddressSanitizer compile flags" OFF) + +# We handle the sanitizers using targets for the compile options, but modify +# CMAKE_EXE_LINKER_FLAGS for the linker flags because the sanitizers should +# only be linked into the final executable and CMake doesn't support linker +# interface flags before CMake 3.13. +add_library(Sanitizers INTERFACE) + +add_library(_Sanitizers_Address INTERFACE) +add_library(Sanitizers::Address ALIAS _Sanitizers_Address) + +add_library(_Sanitizers_UbInteger INTERFACE) +add_library(Sanitizers::UbInteger ALIAS _Sanitizers_UbInteger) + +add_library(_Sanitizers_UbUndefined INTERFACE) +add_library(Sanitizers::UbUndefined ALIAS _Sanitizers_UbUndefined) + +if (ASAN) + set_property( + TARGET _Sanitizers_Address + APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS + $<$:-fno-omit-frame-pointer -fsanitize=address> + ) + set( + CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address" + ) +endif () + +option(UBSAN_UNDEFINED "Add UBSan undefined behavior compile flags" OFF) +if (UBSAN_UNDEFINED) + set_property( + TARGET _Sanitizers_UbUndefined + APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS + $<$:-fno-omit-frame-pointer -fsanitize=undefined> + ) + set( + CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined" + ) +endif () + +option(UBSAN_INTEGER "Add UBSan unsigned integer overflow compile flags" OFF) +if (UBSAN_INTEGER) + set_property( + TARGET _Sanitizers_UbInteger + APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS + $<$:-fno-omit-frame-pointer -fsanitize=integer> + ) + set( + CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=integer" + ) + set( + CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=integer" + ) +endif () + +target_link_libraries( + ElasticaFlags + INTERFACE + _Sanitizers_Address + _Sanitizers_UbInteger + _Sanitizers_UbUndefined +) diff --git a/backend/cmake/SetupSleef.cmake b/backend/cmake/SetupSleef.cmake new file mode 100644 index 000000000..28f548499 --- /dev/null +++ b/backend/cmake/SetupSleef.cmake @@ -0,0 +1,28 @@ +log_info("Finding Sleef") + +find_package(Sleef QUIET) + +if (SLEEF_FOUND) + file(APPEND "${CMAKE_BINARY_DIR}/BuildInformation.txt" + "Sleef Version: ${SLEEF_VERSION}\n") + + add_library(Sleef INTERFACE IMPORTED) + set_property(TARGET Sleef PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${SLEEF_INCLUDE_DIRS}) + set_property(TARGET Sleef + APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${SLEEF_LIBRARIES}) + add_interface_lib_headers( + TARGET Sleef + HEADERS + sleef.h + ) + + set_property( + GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS + Sleef + ) + + log_debug("SLEEF_INCLUDE_DIRS: ${SLEEF_INCLUDE_DIRS}") + log_debug("SLEEF_LIBRARIES: ${SLEEF_LIBRARIES}") + log_debug("SLEEF_VERSION: ${SLEEF_VERSION}") +endif () diff --git a/backend/cmake/SetupStl.cmake b/backend/cmake/SetupStl.cmake new file mode 100644 index 000000000..a1c1083a2 --- /dev/null +++ b/backend/cmake/SetupStl.cmake @@ -0,0 +1,123 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. + +# Create an C++ standard library target for tracking dependencies +# through includes throughout Elastica. +if (NOT TARGET Stl) + add_library(Stl INTERFACE IMPORTED) + + add_interface_lib_headers( + TARGET Stl + HEADERS + algorithm + any + array + atomic + barrier + bit + bitset + cassert + cctype + cerrno + cfenv + cfloat + charconv + chrono + cinttypes + climits + clocale + cmath + codecvt + compare + complex + concepts + condition_variable + coroutine + csetjmp + csignal + cstdarg + cstddef + cstdint + cstdio + cstdlib + cstring + ctime + cuchar + cwchar + cwctype + deque + exception + execution + format + forward_list + fstream + functional + future + initializer_list + iomanip + ios + iosfwd + iostream + istream + iterator + latch + limits + list + locale + map + memory + memory_resource + mutex + new + numbers + numeric + optional + ostream + queue + random + ranges + ratio + regex + scoped_allocator + semaphore + set + shared_mutex + source_location + span + sstream + stack + stdexcept + stop_token + streambuf + string + string_view + strstream + syncstream + system_error + thread + tuple + type_traits + typeindex + typeinfo + unordered_map + unordered_set + utility + valarray + variant + vector + version + + # UNIX/Linux specific headers + dirent.h + libgen.h + sys/stat.h + sys/types.h + unistd.h + xmmintrin.h + ) + + set_property( + GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS + Stl + ) +endif (NOT TARGET Stl) diff --git a/backend/cmake/SetupTBB.cmake b/backend/cmake/SetupTBB.cmake new file mode 100644 index 000000000..531c69d2d --- /dev/null +++ b/backend/cmake/SetupTBB.cmake @@ -0,0 +1,34 @@ +# Setup TBB in elastica environment + +# Distributed under the MIT License. See LICENSE for details. + +log_info("Finding TBB") + +# TBB >= 2021 required because variadic support was added in this version +# for parallel invoke, which is needed by some of RunTests. +find_package(TBB 2021.0 REQUIRED) + +file(APPEND "${CMAKE_BINARY_DIR}/BuildInformation.txt" + "TBB Version: ${TBB_VERSION}\n") + +add_library(TBB INTERFACE IMPORTED) +set_property(TARGET TBB + APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} ${TBB_MALLOC_INCLUDE_DIRS}) +set_property(TARGET TBB + APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${TBB_LIBRARIES} ${TBB_MALLOC_LIBRARIES}) + +set_property( + GLOBAL APPEND PROPERTY ELASTICA_THIRD_PARTY_LIBS + TBB +) + +#elastica_include_directories("${TBB_INCLUDE_DIRS}") +#elastica_include_directories("${TBB_MALLOC_INCLUDE_DIRS}") +#list(APPEND ELASTICA_LIBRARIES ${TBB_LIBRARIES}) +#list(APPEND ELASTICA_LIBRARIES ${TBB_MALLOC_LIBRARIES}) + +log_debug("TBB_INCLUDE_DIRS: ${TBB_INCLUDE_DIRS}") +log_debug("TBB_LIBRARIES: ${TBB_LIBRARIES}") +log_debug("TBB_MALLOC_INCLUDE_DIRS: ${TBB_MALLOC_INCLUDE_DIRS}") +log_debug("TBB_MALLOC_LIBRARIES: ${TBB_MALLOC_LIBRARIES}") +log_debug("TBB_VERSION: ${TBB_VERSION}") diff --git a/backend/pyproject.toml b/backend/pyproject.toml new file mode 100644 index 000000000..fd92373c6 --- /dev/null +++ b/backend/pyproject.toml @@ -0,0 +1,50 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel", + "ninja", + "cmake>=3.12", +] +build-backend = "setuptools.build_meta" + +[tool.mypy] +files = "setup.py" +python_version = "3.7" +strict = true +show_error_codes = true +enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] +warn_unreachable = true + +[[tool.mypy.overrides]] +module = ["ninja"] +ignore_missing_imports = true + + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] +xfail_strict = true +filterwarnings = [ + "error", + "ignore:(ast.Str|Attribute s|ast.NameConstant|ast.Num) is deprecated:DeprecationWarning:_pytest", +] +testpaths = ["tests"] + +[tool.cibuildwheel] +test-command = "pytest {project}/tests" +test-extras = ["test"] +test-skip = ["*universal2:arm64"] +# Setuptools bug causes collision between pypy and cpython artifacts +before-build = "rm -rf {project}/build" + +[tool.ruff] +target-version = "py37" + +[tool.ruff.lint] +extend-select = [ + "B", # flake8-bugbear + "I", # isort + "PGH", # pygrep-hooks + "RUF", # Ruff-specific + "UP", # pyupgrade +] diff --git a/backend/setup.py b/backend/setup.py new file mode 100644 index 000000000..80240487e --- /dev/null +++ b/backend/setup.py @@ -0,0 +1,139 @@ +import os +import re +import subprocess +import sys +from pathlib import Path + +from setuptools import Extension, setup +from setuptools.command.build_ext import build_ext + +# Convert distutils Windows platform specifiers to CMake -A arguments +PLAT_TO_CMAKE = { + "win32": "Win32", + "win-amd64": "x64", + "win-arm32": "ARM", + "win-arm64": "ARM64", +} + + +# A CMakeExtension needs a sourcedir instead of a file list. +# The name must be the _single_ output extension from the CMake build. +# If you need multiple extensions, see scikit-build. +class CMakeExtension(Extension): + def __init__(self, name: str, sourcedir: str = "") -> None: + super().__init__(name, sources=[]) + self.sourcedir = os.fspath(Path(sourcedir).resolve()) + + +class CMakeBuild(build_ext): + def build_extension(self, ext: CMakeExtension) -> None: + # Must be in this form due to bug in .resolve() only fixed in Python 3.10+ + ext_fullpath = Path.cwd() / self.get_ext_fullpath(ext.name) + extdir = ext_fullpath.parent.resolve() + + # Using this requires trailing slash for auto-detection & inclusion of + # auxiliary "native" libs + + debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug + cfg = "Debug" if debug else "Release" + + # CMake lets you override the generator - we need to check this. + # Can be set with Conda-Build, for example. + cmake_generator = os.environ.get("CMAKE_GENERATOR", "") + + # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON + # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code + # from Python. + cmake_args = [ + f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", + f"-DPYTHON_EXECUTABLE={sys.executable}", + f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm + ] + build_args = [] + # Adding CMake arguments set as environment variable + # (needed e.g. to build for ARM OSx on conda-forge) + if "CMAKE_ARGS" in os.environ: + cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item] + + # In this example, we pass in the version to C++. You might not need to. + cmake_args += [f"-DEXAMPLE_VERSION_INFO={self.distribution.get_version()}"] + + if self.compiler.compiler_type != "msvc": + # Using Ninja-build since it a) is available as a wheel and b) + # multithreads automatically. MSVC would require all variables be + # exported for Ninja to pick it up, which is a little tricky to do. + # Users can override the generator with CMAKE_GENERATOR in CMake + # 3.15+. + if not cmake_generator or cmake_generator == "Ninja": + try: + import ninja + + ninja_executable_path = Path(ninja.BIN_DIR) / "ninja" + cmake_args += [ + "-GNinja", + f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}", + ] + except ImportError: + pass + + else: + # Single config generators are handled "normally" + single_config = any(x in cmake_generator for x in {"NMake", "Ninja"}) + + # CMake allows an arch-in-generator style for backward compatibility + contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) + + # Specify the arch if using MSVC generator, but only if it doesn't + # contain a backward-compatibility arch spec already in the + # generator name. + if not single_config and not contains_arch: + cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] + + # Multi-config generators have a different way to specify configs + if not single_config: + cmake_args += [ + f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}" + ] + build_args += ["--config", cfg] + + if sys.platform.startswith("darwin"): + # Cross-compile support for macOS - respect ARCHFLAGS if set + archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) + if archs: + cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] + + # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level + # across all generators. + if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ: + # self.parallel is a Python 3 only way to set parallel jobs by hand + # using -j in the build_ext call, not supported by pip or PyPA-build. + if hasattr(self, "parallel") and self.parallel: + # CMake 3.12+ only. + build_args += [f"-j{self.parallel}"] + + build_temp = Path(self.build_temp) / ext.name + if not build_temp.exists(): + build_temp.mkdir(parents=True) + + subprocess.run( + ["cmake", ext.sourcedir, *cmake_args], cwd=build_temp, check=True + ) + subprocess.run( + ["cmake", "--build", ".", *build_args], cwd=build_temp, check=True + ) + + +# The information here can also be placed in setup.cfg - better separation of +# logic and declaration, and simpler if you include description/version in a file. +setup( + name="elasticapp", + version="0.0.3", + author="Seung Hyun Kim", + author_email="skim0119@gmail.com", + description="A CPP accelerated backend for PyElastica kernels", + long_description="", + ext_modules=[CMakeExtension("elasticapp")], + cmdclass={"build_ext": CMakeBuild}, + zip_safe=False, + python_requires=">=3.10", +) diff --git a/poetry.lock b/poetry.lock index 012ac94f5..fbe0bfd24 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,18 +2,22 @@ [[package]] name = "accessible-pygments" -version = "0.0.4" +version = "0.0.5" description = "A collection of accessible pygments styles" optional = true -python-versions = "*" +python-versions = ">=3.9" files = [ - {file = "accessible-pygments-0.0.4.tar.gz", hash = "sha256:e7b57a9b15958e9601c7e9eb07a440c813283545a20973f2574a5f453d0e953e"}, - {file = "accessible_pygments-0.0.4-py2.py3-none-any.whl", hash = "sha256:416c6d8c1ea1c5ad8701903a20fcedf953c6e720d64f33dc47bfb2d3f2fa4e8d"}, + {file = "accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7"}, + {file = "accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872"}, ] [package.dependencies] pygments = ">=1.5" +[package.extras] +dev = ["pillow", "pkginfo (>=1.10)", "playwright", "pre-commit", "setuptools", "twine (>=5.0)"] +tests = ["hypothesis", "pytest"] + [[package]] name = "alabaster" version = "0.7.16" @@ -41,13 +45,13 @@ pyflakes = ">=2.3.0" [[package]] name = "babel" -version = "2.14.0" +version = "2.15.0" description = "Internationalization utilities" optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, + {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, + {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, ] [package.extras] @@ -460,13 +464,13 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.13.4" +version = "3.14.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, - {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, ] [package.extras] @@ -492,53 +496,53 @@ pyflakes = ">=2.3.0,<2.4.0" [[package]] name = "fonttools" -version = "4.51.0" +version = "4.52.1" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:84d7751f4468dd8cdd03ddada18b8b0857a5beec80bce9f435742abc9a851a74"}, - {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8b4850fa2ef2cfbc1d1f689bc159ef0f45d8d83298c1425838095bf53ef46308"}, - {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5b48a1121117047d82695d276c2af2ee3a24ffe0f502ed581acc2673ecf1037"}, - {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:180194c7fe60c989bb627d7ed5011f2bef1c4d36ecf3ec64daec8302f1ae0716"}, - {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:96a48e137c36be55e68845fc4284533bda2980f8d6f835e26bca79d7e2006438"}, - {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:806e7912c32a657fa39d2d6eb1d3012d35f841387c8fc6cf349ed70b7c340039"}, - {file = "fonttools-4.51.0-cp310-cp310-win32.whl", hash = "sha256:32b17504696f605e9e960647c5f64b35704782a502cc26a37b800b4d69ff3c77"}, - {file = "fonttools-4.51.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7e91abdfae1b5c9e3a543f48ce96013f9a08c6c9668f1e6be0beabf0a569c1b"}, - {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a8feca65bab31479d795b0d16c9a9852902e3a3c0630678efb0b2b7941ea9c74"}, - {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ac27f436e8af7779f0bb4d5425aa3535270494d3bc5459ed27de3f03151e4c2"}, - {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e19bd9e9964a09cd2433a4b100ca7f34e34731e0758e13ba9a1ed6e5468cc0f"}, - {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2b92381f37b39ba2fc98c3a45a9d6383bfc9916a87d66ccb6553f7bdd129097"}, - {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5f6bc991d1610f5c3bbe997b0233cbc234b8e82fa99fc0b2932dc1ca5e5afec0"}, - {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9696fe9f3f0c32e9a321d5268208a7cc9205a52f99b89479d1b035ed54c923f1"}, - {file = "fonttools-4.51.0-cp311-cp311-win32.whl", hash = "sha256:3bee3f3bd9fa1d5ee616ccfd13b27ca605c2b4270e45715bd2883e9504735034"}, - {file = "fonttools-4.51.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f08c901d3866a8905363619e3741c33f0a83a680d92a9f0e575985c2634fcc1"}, - {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4060acc2bfa2d8e98117828a238889f13b6f69d59f4f2d5857eece5277b829ba"}, - {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1250e818b5f8a679ad79660855528120a8f0288f8f30ec88b83db51515411fcc"}, - {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76f1777d8b3386479ffb4a282e74318e730014d86ce60f016908d9801af9ca2a"}, - {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b5ad456813d93b9c4b7ee55302208db2b45324315129d85275c01f5cb7e61a2"}, - {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:68b3fb7775a923be73e739f92f7e8a72725fd333eab24834041365d2278c3671"}, - {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8e2f1a4499e3b5ee82c19b5ee57f0294673125c65b0a1ff3764ea1f9db2f9ef5"}, - {file = "fonttools-4.51.0-cp312-cp312-win32.whl", hash = "sha256:278e50f6b003c6aed19bae2242b364e575bcb16304b53f2b64f6551b9c000e15"}, - {file = "fonttools-4.51.0-cp312-cp312-win_amd64.whl", hash = "sha256:b3c61423f22165541b9403ee39874dcae84cd57a9078b82e1dce8cb06b07fa2e"}, - {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1621ee57da887c17312acc4b0e7ac30d3a4fb0fec6174b2e3754a74c26bbed1e"}, - {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d9298be7a05bb4801f558522adbe2feea1b0b103d5294ebf24a92dd49b78e5"}, - {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee1af4be1c5afe4c96ca23badd368d8dc75f611887fb0c0dac9f71ee5d6f110e"}, - {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c18b49adc721a7d0b8dfe7c3130c89b8704baf599fb396396d07d4aa69b824a1"}, - {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de7c29bdbdd35811f14493ffd2534b88f0ce1b9065316433b22d63ca1cd21f14"}, - {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cadf4e12a608ef1d13e039864f484c8a968840afa0258b0b843a0556497ea9ed"}, - {file = "fonttools-4.51.0-cp38-cp38-win32.whl", hash = "sha256:aefa011207ed36cd280babfaa8510b8176f1a77261833e895a9d96e57e44802f"}, - {file = "fonttools-4.51.0-cp38-cp38-win_amd64.whl", hash = "sha256:865a58b6e60b0938874af0968cd0553bcd88e0b2cb6e588727117bd099eef836"}, - {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:60a3409c9112aec02d5fb546f557bca6efa773dcb32ac147c6baf5f742e6258b"}, - {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7e89853d8bea103c8e3514b9f9dc86b5b4120afb4583b57eb10dfa5afbe0936"}, - {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56fc244f2585d6c00b9bcc59e6593e646cf095a96fe68d62cd4da53dd1287b55"}, - {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d145976194a5242fdd22df18a1b451481a88071feadf251221af110ca8f00ce"}, - {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5b8cab0c137ca229433570151b5c1fc6af212680b58b15abd797dcdd9dd5051"}, - {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:54dcf21a2f2d06ded676e3c3f9f74b2bafded3a8ff12f0983160b13e9f2fb4a7"}, - {file = "fonttools-4.51.0-cp39-cp39-win32.whl", hash = "sha256:0118ef998a0699a96c7b28457f15546815015a2710a1b23a7bf6c1be60c01636"}, - {file = "fonttools-4.51.0-cp39-cp39-win_amd64.whl", hash = "sha256:599bdb75e220241cedc6faebfafedd7670335d2e29620d207dd0378a4e9ccc5a"}, - {file = "fonttools-4.51.0-py3-none-any.whl", hash = "sha256:15c94eeef6b095831067f72c825eb0e2d48bb4cea0647c1b05c981ecba2bf39f"}, - {file = "fonttools-4.51.0.tar.gz", hash = "sha256:dc0673361331566d7a663d7ce0f6fdcbfbdc1f59c6e3ed1165ad7202ca183c68"}, + {file = "fonttools-4.52.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:67a30b872e79577e5319ce660ede4a5131fa8a45de76e696746545e17db4437f"}, + {file = "fonttools-4.52.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a5bff35738f8f6607c4303561ee1d1e5f64d5b14cf3c472d3030566c82e763"}, + {file = "fonttools-4.52.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c9622593dfff042480a1b7e5b72c4d7dc00b96d2b4f98b0bf8acf071087e0db"}, + {file = "fonttools-4.52.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33cfc9fe27af5e113d157d5147e24fc8e5bda3c5aadb55bea9847ec55341ce30"}, + {file = "fonttools-4.52.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aa5bec5027d947ee4b2242caecf7dc6e4ea03833e92e9b5211ebb6ab4eede8b2"}, + {file = "fonttools-4.52.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10e44bf8e5654050a332a79285bacd6bd3069084540aec46c0862391147a1daa"}, + {file = "fonttools-4.52.1-cp310-cp310-win32.whl", hash = "sha256:7fba390ac2ca18ebdd456f3a9acfb4557d6dcb2eaba5cc3eadce01003892a770"}, + {file = "fonttools-4.52.1-cp310-cp310-win_amd64.whl", hash = "sha256:15df3517eb95035422a5c953ca19aac99913c16aa0e4ef061aeaef5f3bcaf369"}, + {file = "fonttools-4.52.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:40730aab9cf42286f314b985b483eea574f1bcf3a23e28223084cbb9e256457c"}, + {file = "fonttools-4.52.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a19bc2be3af5b22ff5c7fe858c380862e31052c74f62e2c6d565ed0855bed7a6"}, + {file = "fonttools-4.52.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f859066d8afde53f2ddabcd0705061e6d9d9868757c6ae28abe49bc885292df4"}, + {file = "fonttools-4.52.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74cd3e3e9ba501e87a391b62e91f7b1610e8b3f3d706a368e5aee51614c1674e"}, + {file = "fonttools-4.52.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:958957b81418647f66820480363cb617ba6b5bcf189ec6c4cea307d051048545"}, + {file = "fonttools-4.52.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56addf1f995d94dad13aaaf56eb6def3d9ca97c2fada5e27af8190b3141e8633"}, + {file = "fonttools-4.52.1-cp311-cp311-win32.whl", hash = "sha256:fea5456b2af42db8ecb1a6c2f144655ca6dcdcebd970f3145c56e668084ded7e"}, + {file = "fonttools-4.52.1-cp311-cp311-win_amd64.whl", hash = "sha256:228faab7638cd726cdde5e2ec9ee10f780fbf9de9aa38d7f1e56a270437dff36"}, + {file = "fonttools-4.52.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7c6aeb0d53e2ea92009b11c3d4ad9c03d0ecdfe602d547bed8537836e464f51e"}, + {file = "fonttools-4.52.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e871123d12c92e2c9bda6369b69ce2da9cef40b119cc340451e413e90355fa38"}, + {file = "fonttools-4.52.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ff8857dc9bb3e407c25aef3e025409cfbb23adb646a835636bebb1bdfc27a41"}, + {file = "fonttools-4.52.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7685fdc6e23267844eef2b9af585d7f171cca695e4eb369d7682544c3e2e1123"}, + {file = "fonttools-4.52.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1e1b2774485fbbb41a1beccc913b9c6f7971f78da61dd34207b9acc3cc2963e"}, + {file = "fonttools-4.52.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1e2c415160397fd6ed3964155aeec4bfefceeee365ab17161a5b3fe3f8dab077"}, + {file = "fonttools-4.52.1-cp312-cp312-win32.whl", hash = "sha256:3ba2c4647e7decfb8e9cd346661c7d151dae1fba23d37b48bcf5fa8351f7b8c8"}, + {file = "fonttools-4.52.1-cp312-cp312-win_amd64.whl", hash = "sha256:d39b926f14a2f7a7f92ded7d266b18f0108d867364769ab59da88ac2fa90d288"}, + {file = "fonttools-4.52.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6e58d8097a269b6c43ec0abb3fa8d6c350ff0c7dfd23fc14d004610df88a4bb3"}, + {file = "fonttools-4.52.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:20f0fc969817c50539dc919ed8c4aef4de28c2d6e0111a064112301f157aede4"}, + {file = "fonttools-4.52.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d62e84d38969491c6c1f6fe3dd63108e99d02de01bb3d98c160a5d4d24120910"}, + {file = "fonttools-4.52.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8eb5a389bbdee6f4c422881de422ee0e7efdfcd9310b13d540b12aa8ae2c9e7b"}, + {file = "fonttools-4.52.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0caf05c969cbde6729dd97b64bea445ee152bb19215d5886f7b93bd0fb455468"}, + {file = "fonttools-4.52.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:df08bee1dd29a767311b50c62c0cfe4d72ae8c793e567d4c60b8c16c7c63a4f0"}, + {file = "fonttools-4.52.1-cp38-cp38-win32.whl", hash = "sha256:82ffcf4782ceda09842b5b7875b36834c15d7cc0d5dd3d23a658ee9cf8819cd6"}, + {file = "fonttools-4.52.1-cp38-cp38-win_amd64.whl", hash = "sha256:26b43bab5a3bce55ed4d9699b16568795eef5597d154f52dcabef5b4804c4b21"}, + {file = "fonttools-4.52.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e8dbc13c4bc12e60df1b1f5e484112a5e96a6e8bba995e2965988ad73c5ea1b"}, + {file = "fonttools-4.52.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7352ba2226e45e8fba11c3fb416363faf1b06f3f2e80d07d2930401265f3bf9c"}, + {file = "fonttools-4.52.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8834d43763e9e92349ce8bb25dfb612aef6691eefefad885212d5e8f36a94a4"}, + {file = "fonttools-4.52.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee2a8c1101d06cc8fca7851dceb67afd53dd6fc0288bacaa632e647bc5afff58"}, + {file = "fonttools-4.52.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a99b738227c0f6f2bbe381b45804a7c46653c95b9d7bf13f6f02884bc87e4930"}, + {file = "fonttools-4.52.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:75aa00a16b9a64d1550e2e70d3582c7fe1ef18560e0cf066a4087fe6d11908a2"}, + {file = "fonttools-4.52.1-cp39-cp39-win32.whl", hash = "sha256:c2f09b4aa699cfed4bbebc1829c5f044b41976707dac9230ed00d5a9fc6452c1"}, + {file = "fonttools-4.52.1-cp39-cp39-win_amd64.whl", hash = "sha256:78ea6e0d4c89f8e216995923b854dd10bd09e48d3a5a3ccb48bb68f436a409ad"}, + {file = "fonttools-4.52.1-py3-none-any.whl", hash = "sha256:faf5c83f83f7ddebdafdb453d02efdbea7fb494080d7a8d45a8a20db06ea8da5"}, + {file = "fonttools-4.52.1.tar.gz", hash = "sha256:8c9204435aa6e5e9479a5ba4e669f05dea28b0c61958e0c0923cb164296d9329"}, ] [package.extras] @@ -606,7 +610,7 @@ files = [ name = "jinja2" version = "3.1.4" description = "A very fast and expressive template engine." -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, @@ -793,7 +797,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, @@ -860,39 +864,40 @@ files = [ [[package]] name = "matplotlib" -version = "3.8.4" +version = "3.9.0" description = "Python plotting package" optional = false python-versions = ">=3.9" files = [ - {file = "matplotlib-3.8.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:abc9d838f93583650c35eca41cfcec65b2e7cb50fd486da6f0c49b5e1ed23014"}, - {file = "matplotlib-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f65c9f002d281a6e904976007b2d46a1ee2bcea3a68a8c12dda24709ddc9106"}, - {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce1edd9f5383b504dbc26eeea404ed0a00656c526638129028b758fd43fc5f10"}, - {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd79298550cba13a43c340581a3ec9c707bd895a6a061a78fa2524660482fc0"}, - {file = "matplotlib-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:90df07db7b599fe7035d2f74ab7e438b656528c68ba6bb59b7dc46af39ee48ef"}, - {file = "matplotlib-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:ac24233e8f2939ac4fd2919eed1e9c0871eac8057666070e94cbf0b33dd9c338"}, - {file = "matplotlib-3.8.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:72f9322712e4562e792b2961971891b9fbbb0e525011e09ea0d1f416c4645661"}, - {file = "matplotlib-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:232ce322bfd020a434caaffbd9a95333f7c2491e59cfc014041d95e38ab90d1c"}, - {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6addbd5b488aedb7f9bc19f91cd87ea476206f45d7116fcfe3d31416702a82fa"}, - {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4ccdc64e3039fc303defd119658148f2349239871db72cd74e2eeaa9b80b71"}, - {file = "matplotlib-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b7a2a253d3b36d90c8993b4620183b55665a429da8357a4f621e78cd48b2b30b"}, - {file = "matplotlib-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:8080d5081a86e690d7688ffa542532e87f224c38a6ed71f8fbed34dd1d9fedae"}, - {file = "matplotlib-3.8.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6485ac1f2e84676cff22e693eaa4fbed50ef5dc37173ce1f023daef4687df616"}, - {file = "matplotlib-3.8.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c89ee9314ef48c72fe92ce55c4e95f2f39d70208f9f1d9db4e64079420d8d732"}, - {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50bac6e4d77e4262c4340d7a985c30912054745ec99756ce213bfbc3cb3808eb"}, - {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f51c4c869d4b60d769f7b4406eec39596648d9d70246428745a681c327a8ad30"}, - {file = "matplotlib-3.8.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b12ba985837e4899b762b81f5b2845bd1a28f4fdd1a126d9ace64e9c4eb2fb25"}, - {file = "matplotlib-3.8.4-cp312-cp312-win_amd64.whl", hash = "sha256:7a6769f58ce51791b4cb8b4d7642489df347697cd3e23d88266aaaee93b41d9a"}, - {file = "matplotlib-3.8.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:843cbde2f0946dadd8c5c11c6d91847abd18ec76859dc319362a0964493f0ba6"}, - {file = "matplotlib-3.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c13f041a7178f9780fb61cc3a2b10423d5e125480e4be51beaf62b172413b67"}, - {file = "matplotlib-3.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb44f53af0a62dc80bba4443d9b27f2fde6acfdac281d95bc872dc148a6509cc"}, - {file = "matplotlib-3.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:606e3b90897554c989b1e38a258c626d46c873523de432b1462f295db13de6f9"}, - {file = "matplotlib-3.8.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9bb0189011785ea794ee827b68777db3ca3f93f3e339ea4d920315a0e5a78d54"}, - {file = "matplotlib-3.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:6209e5c9aaccc056e63b547a8152661324404dd92340a6e479b3a7f24b42a5d0"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c7064120a59ce6f64103c9cefba8ffe6fba87f2c61d67c401186423c9a20fd35"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0e47eda4eb2614300fc7bb4657fced3e83d6334d03da2173b09e447418d499f"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:493e9f6aa5819156b58fce42b296ea31969f2aab71c5b680b4ea7a3cb5c07d94"}, - {file = "matplotlib-3.8.4.tar.gz", hash = "sha256:8aac397d5e9ec158960e31c381c5ffc52ddd52bd9a47717e2a694038167dffea"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2bcee1dffaf60fe7656183ac2190bd630842ff87b3153afb3e384d966b57fe56"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f988bafb0fa39d1074ddd5bacd958c853e11def40800c5824556eb630f94d3b"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe428e191ea016bb278758c8ee82a8129c51d81d8c4bc0846c09e7e8e9057241"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf3978060a106fab40c328778b148f590e27f6fa3cd15a19d6892575bce387d"}, + {file = "matplotlib-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e7f03e5cbbfacdd48c8ea394d365d91ee8f3cae7e6ec611409927b5ed997ee4"}, + {file = "matplotlib-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:13beb4840317d45ffd4183a778685e215939be7b08616f431c7795276e067463"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:063af8587fceeac13b0936c42a2b6c732c2ab1c98d38abc3337e430e1ff75e38"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a2fa6d899e17ddca6d6526cf6e7ba677738bf2a6a9590d702c277204a7c6152"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550cdda3adbd596078cca7d13ed50b77879104e2e46392dcd7c75259d8f00e85"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cce0f31b351e3551d1f3779420cf8f6ec0d4a8cf9c0237a3b549fd28eb4abb"}, + {file = "matplotlib-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c53aeb514ccbbcbab55a27f912d79ea30ab21ee0531ee2c09f13800efb272674"}, + {file = "matplotlib-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5be985db2596d761cdf0c2eaf52396f26e6a64ab46bd8cd810c48972349d1be"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c79f3a585f1368da6049318bdf1f85568d8d04b2e89fc24b7e02cc9b62017382"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdd1ecbe268eb3e7653e04f451635f0fb0f77f07fd070242b44c076c9106da84"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e85a1a6d732f645f1403ce5e6727fd9418cd4574521d5803d3d94911038e5"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a490715b3b9984fa609116481b22178348c1a220a4499cda79132000a79b4db"}, + {file = "matplotlib-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8146ce83cbc5dc71c223a74a1996d446cd35cfb6a04b683e1446b7e6c73603b7"}, + {file = "matplotlib-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:d91a4ffc587bacf5c4ce4ecfe4bcd23a4b675e76315f2866e588686cc97fccdf"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:616fabf4981a3b3c5a15cd95eba359c8489c4e20e03717aea42866d8d0465956"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd53c79fd02f1c1808d2cfc87dd3cf4dbc63c5244a58ee7944497107469c8d8a"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06a478f0d67636554fa78558cfbcd7b9dba85b51f5c3b5a0c9be49010cf5f321"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81c40af649d19c85f8073e25e5806926986806fa6d54be506fbf02aef47d5a89"}, + {file = "matplotlib-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52146fc3bd7813cc784562cb93a15788be0b2875c4655e2cc6ea646bfa30344b"}, + {file = "matplotlib-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:0fc51eaa5262553868461c083d9adadb11a6017315f3a757fc45ec6ec5f02888"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bd4f2831168afac55b881db82a7730992aa41c4f007f1913465fb182d6fb20c0"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:290d304e59be2b33ef5c2d768d0237f5bd132986bdcc66f80bc9bcc300066a03"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff2e239c26be4f24bfa45860c20ffccd118d270c5b5d081fa4ea409b5469fcd"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:af4001b7cae70f7eaacfb063db605280058246de590fa7874f00f62259f2df7e"}, + {file = "matplotlib-3.9.0.tar.gz", hash = "sha256:e6d29ea6c19e34b30fb7d88b7081f869a03014f66fe06d62cc77d5a6ea88ed7a"}, ] [package.dependencies] @@ -900,12 +905,15 @@ contourpy = ">=1.0.1" cycler = ">=0.10" fonttools = ">=4.22.0" kiwisolver = ">=1.3.1" -numpy = ">=1.21" +numpy = ">=1.23" packaging = ">=20.0" pillow = ">=8" pyparsing = ">=2.3.1" python-dateutil = ">=2.7" +[package.extras] +dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] + [[package]] name = "mccabe" version = "0.6.1" @@ -1203,13 +1211,13 @@ xmp = ["defusedxml"] [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] @@ -1282,6 +1290,20 @@ files = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] +[[package]] +name = "pybind11" +version = "2.12.0" +description = "Seamless operability between C++11 and Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pybind11-2.12.0-py3-none-any.whl", hash = "sha256:df8d60b94f9e714d81013db233393d430ebf9f3551642b82291cf1b14d1afdbd"}, + {file = "pybind11-2.12.0.tar.gz", hash = "sha256:5e3c557a84b06b969247630407fc4d985bed157b4253b13153b8e8e165e0c3dc"}, +] + +[package.extras] +global = ["pybind11-global (==2.12.0)"] + [[package]] name = "pycodestyle" version = "2.7.0" @@ -1333,17 +1355,16 @@ files = [ [[package]] name = "pygments" -version = "2.17.2" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] -plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] @@ -1485,7 +1506,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1493,16 +1513,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1519,7 +1531,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1527,7 +1538,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1546,13 +1556,13 @@ files = [ [[package]] name = "requests" -version = "2.31.0" +version = "2.32.2" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, + {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, ] [package.dependencies] @@ -1567,36 +1577,36 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "scipy" -version = "1.13.0" +version = "1.13.1" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.9" files = [ - {file = "scipy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba419578ab343a4e0a77c0ef82f088238a93eef141b2b8017e46149776dfad4d"}, - {file = "scipy-1.13.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:22789b56a999265431c417d462e5b7f2b487e831ca7bef5edeb56efe4c93f86e"}, - {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f1432ba070e90d42d7fd836462c50bf98bd08bed0aa616c359eed8a04e3922"}, - {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8434f6f3fa49f631fae84afee424e2483289dfc30a47755b4b4e6b07b2633a4"}, - {file = "scipy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dcbb9ea49b0167de4167c40eeee6e167caeef11effb0670b554d10b1e693a8b9"}, - {file = "scipy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:1d2f7bb14c178f8b13ebae93f67e42b0a6b0fc50eba1cd8021c9b6e08e8fb1cd"}, - {file = "scipy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fbcf8abaf5aa2dc8d6400566c1a727aed338b5fe880cde64907596a89d576fa"}, - {file = "scipy-1.13.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5e4a756355522eb60fcd61f8372ac2549073c8788f6114449b37e9e8104f15a5"}, - {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5acd8e1dbd8dbe38d0004b1497019b2dbbc3d70691e65d69615f8a7292865d7"}, - {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ff7dad5d24a8045d836671e082a490848e8639cabb3dbdacb29f943a678683d"}, - {file = "scipy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4dca18c3ffee287ddd3bc8f1dabaf45f5305c5afc9f8ab9cbfab855e70b2df5c"}, - {file = "scipy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:a2f471de4d01200718b2b8927f7d76b5d9bde18047ea0fa8bd15c5ba3f26a1d6"}, - {file = "scipy-1.13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0de696f589681c2802f9090fff730c218f7c51ff49bf252b6a97ec4a5d19e8b"}, - {file = "scipy-1.13.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:b2a3ff461ec4756b7e8e42e1c681077349a038f0686132d623fa404c0bee2551"}, - {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf9fe63e7a4bf01d3645b13ff2aa6dea023d38993f42aaac81a18b1bda7a82a"}, - {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e7626dfd91cdea5714f343ce1176b6c4745155d234f1033584154f60ef1ff42"}, - {file = "scipy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:109d391d720fcebf2fbe008621952b08e52907cf4c8c7efc7376822151820820"}, - {file = "scipy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8930ae3ea371d6b91c203b1032b9600d69c568e537b7988a3073dfe4d4774f21"}, - {file = "scipy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5407708195cb38d70fd2d6bb04b1b9dd5c92297d86e9f9daae1576bd9e06f602"}, - {file = "scipy-1.13.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ac38c4c92951ac0f729c4c48c9e13eb3675d9986cc0c83943784d7390d540c78"}, - {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c74543c4fbeb67af6ce457f6a6a28e5d3739a87f62412e4a16e46f164f0ae5"}, - {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28e286bf9ac422d6beb559bc61312c348ca9b0f0dae0d7c5afde7f722d6ea13d"}, - {file = "scipy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33fde20efc380bd23a78a4d26d59fc8704e9b5fd9b08841693eb46716ba13d86"}, - {file = "scipy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:45c08bec71d3546d606989ba6e7daa6f0992918171e2a6f7fbedfa7361c2de1e"}, - {file = "scipy-1.13.0.tar.gz", hash = "sha256:58569af537ea29d3f78e5abd18398459f195546bb3be23d16677fb26616cc11e"}, + {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, + {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, + {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, + {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, + {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, + {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, + {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, + {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, + {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, + {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, + {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, ] [package.dependencies] @@ -1609,13 +1619,13 @@ test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "po [[package]] name = "scooby" -version = "0.9.2" +version = "0.10.0" description = "A Great Dane turned Python environment detective" optional = false python-versions = ">=3.8" files = [ - {file = "scooby-0.9.2-py3-none-any.whl", hash = "sha256:3cbc59de9febf8c8ba1e01bc7d08b4eca18ece3212d38b08a6f45188f88c8ea9"}, - {file = "scooby-0.9.2.tar.gz", hash = "sha256:28df643bb7c2087547b2e2220070e2f89e815ddbc515fbc28dd5df2b0a14293e"}, + {file = "scooby-0.10.0-py3-none-any.whl", hash = "sha256:0a3d7e304f8ebb16f69ff7f6360c345d7f50b45f2ddbf7c3d18a6a0dc2cb03a6"}, + {file = "scooby-0.10.0.tar.gz", hash = "sha256:7ea33c262c0cc6a33c6eeeb5648df787be4f22660e53c114e5fff1b811a8854f"}, ] [package.extras] @@ -1623,19 +1633,18 @@ cpu = ["mkl", "psutil"] [[package]] name = "setuptools" -version = "69.5.1" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -1864,13 +1873,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.3" +version = "4.66.4" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53"}, - {file = "tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5"}, + {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, + {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, ] [package.dependencies] @@ -1884,13 +1893,13 @@ telegram = ["requests"] [[package]] name = "typing-extensions" -version = "4.11.0" +version = "4.12.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, + {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, ] [[package]] @@ -1912,13 +1921,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.0" +version = "20.26.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.0-py3-none-any.whl", hash = "sha256:0846377ea76e818daaa3e00a4365c018bc3ac9760cbb3544de542885aad61fb3"}, - {file = "virtualenv-20.26.0.tar.gz", hash = "sha256:ec25a9671a5102c8d2657f62792a27b48f016664c6873f6beed3800008577210"}, + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, ] [package.dependencies] @@ -1978,4 +1987,4 @@ examples = ["cma"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.12" -content-hash = "1345805f189df83c1d4f3e244094e03fa602499461ced2eaf15e85d27780c762" +content-hash = "3d213e76a1b752536a420e8449537dd78b2d0b841d896a8155e8f5962dc18a19" diff --git a/pyproject.toml b/pyproject.toml index 7661ef7b1..6fc107fa0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ myst-parser = {version = "^1.0", optional = true} numpydoc = {version = "^1.3.1", optional = true} docutils = {version = "^0.18", optional = true} cma = {version = "^3.2.2", optional = true} +pybind11 = "^2.12.0" [tool.poetry.dev-dependencies] black = "21.12b0" @@ -91,6 +92,7 @@ exclude = ''' | \.mypy_cache | \.tox | \.venv + | backend | _build | buck-out | build @@ -103,4 +105,4 @@ exclude = ''' [tool.pytest.ini_options] # https://docs.pytest.org/en/6.2.x/customize.html#pyproject-toml # Directories that are not visited by pytest collector: -norecursedirs =["hooks", "*.egg", ".eggs", "dist", "build", "docs", ".tox", ".git", "__pycache__"] +norecursedirs =["hooks", "*.egg", ".eggs", "dist", "build", "docs", ".tox", ".git", "__pycache__", "backend"] From 3a1f10680f919d553ae337ae6fa0f251d6fc6c9c Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Sat, 25 May 2024 15:25:55 -0500 Subject: [PATCH 002/121] update: gitignore file to include c++ code --- .gitignore | 1 + backend/.gitignore | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 backend/.gitignore diff --git a/.gitignore b/.gitignore index 62305a521..535b8659c 100644 --- a/.gitignore +++ b/.gitignore @@ -213,6 +213,7 @@ sample_prog.py # txt files *.txt +!CMakelists.txt # movie or video file formats *.mp4 diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 000000000..46f42f8f3 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,11 @@ +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps From 558cabc7626ebee8c4c53104cbcb263113f59c12 Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Tue, 28 May 2024 13:39:38 -0500 Subject: [PATCH 003/121] included cmakelists --- backend/CMakeLists.txt | 113 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 backend/CMakeLists.txt diff --git a/backend/CMakeLists.txt b/backend/CMakeLists.txt new file mode 100644 index 000000000..ad6f1a798 --- /dev/null +++ b/backend/CMakeLists.txt @@ -0,0 +1,113 @@ +# Automagically build elastica along with its dependencies + +# ############################################################################## +# Distributed under the MIT License. See LICENSE for details. +# ############################################################################## +cmake_minimum_required(VERSION 3.6...3.18) + +# ############################################################################## +# Set policies +# ############################################################################## +if (POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +endif () +if (POLICY CMP0110) + # disable support for arbitrary characters (such as whitespace) in test names + # but rather use underscores + cmake_policy(SET CMP0110 OLD) +endif () + +# ############################################################################## +# Project details +# Versioning +# ############################################################################## +set(ELASTICA_VERSION "0.0.3") +set(ELASTICA_DESCRIPTION "Package to efficiently simulate soft-filaments on devices from your laptop to supercomputing clusters.") + +# C is added for the HDF5 dependency. HighFive also relies on C headers. +project(elastica + LANGUAGES CXX C + VERSION ${ELASTICA_VERSION} + DESCRIPTION ${ELASTICA_DESCRIPTION}) + + +# ############################################################################## +# Setting the directory which contains CMake modules +# ############################################################################## +# Appends the cmake path inside the MAKE_MODULE_PATH variable which stores the +# list of directories where to lookk for additional CMake modules. +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +# Create a new (or overwrite existing) info file at start of configuration +file(WRITE "${CMAKE_BINARY_DIR}/BuildInformation.txt" "") + +# ############################################################################## +# Including cmake only modules +# ############################################################################## +include(Logging) +set_logs_on() +set_log_level("DEBUG") # INFO DEBUG HEAVYDEBUG + +option(ELASTICA_BUILD_PYTHON_BINDINGS "Build the python bindings for elastica" ON) + +# Define standard installation directories before since python targets utilize +include(GNUInstallDirs) + +# Disable `make install` depending on `make all` since we want to control what +# we install more closely. With this setting, and targets marked as `OPTIONAL`, +# only targets that were built will be installed. +set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY ON) + + +include(CheckCompilerVersion) +# +include(ElasticaAddInterfaceLibraryHeaders) +#include(ElasticaTargetHeaders) +#include(ElasticaTargetSources) +#include(ElasticaAddLibraries) +include(ElasticaSetupFlagsTarget) +include(EnableWarnings) +# need flags to be defined + +include(SetBuildType) +#include(SetupCraySupport) +include(SetCxxStandard) +include(SetupCxxFlags) +include(SetupElasticaInlining) +include(SetupMacOsx) +include(SetOutputDirectory) +include(SetupPic) +include(SetupSanitizers) + +# In order to use certain code analysis tools like clang-tidy and cppcheck the +# compile commands need to be accessible. CMake can write these to a +# "compile_commands.json" file. +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Need to load python libs before boost.python. include(SetupPythonLibs) +# include(SetupBlas) include(SetupBoost) include(SetupBrigand) +# include(SetupCatch) include(SetupGoogleBenchmark) include(SetupGsl) +# include(SetupHdf5) include(SetupAllocator) include(SetupLIBXSMM) +# include(SetupLapack) include(SetupNumPy) include(SetupOpenMP) +# include(SetupPapi) include(SetupSciPy) include(SetupLibsharp) + +include(SetupBlaze) +include(SetupBlazeTensor) +#include(SetupBrigand) +#include(SetupProfiling) +#include(SetupHighFive) +#include(SetupSMP) +include(SetupStl) +#include(SetupYamlCpp) +include(SetupFilesystem) + +if (ELASTICA_BUILD_PYTHON_BINDINGS) + include(SetupPybind11) +endif () +#include(ElasticaSetupPythonPackage) + +#add_subdirectory(elastica) + + +include(PrintUsefulCMakeInfo) +#include(ElasticaInstall) From 7a1fe25e37e71a20df20784e40a2e450456d0081 Mon Sep 17 00:00:00 2001 From: Ankith Date: Thu, 30 May 2024 14:51:53 +0530 Subject: [PATCH 004/121] Switch to scikit_build_core, remove setup.py - Moves the metadata from setup.py to pyproject.toml --- backend/pyproject.toml | 49 ++++++++++----- backend/setup.py | 139 ----------------------------------------- 2 files changed, 32 insertions(+), 156 deletions(-) delete mode 100644 backend/setup.py diff --git a/backend/pyproject.toml b/backend/pyproject.toml index fd92373c6..a7620e73c 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -1,24 +1,39 @@ -[build-system] -requires = [ - "setuptools>=42", - "wheel", - "ninja", - "cmake>=3.12", +[project] +name = "elasticapp" +version = "0.0.3" +description = "A CPP accelerated backend for PyElastica kernels" +# readme = "README.rst" # for long description +requires-python = ">=3.10" +license = {text = "MIT License"} + +# this is a list that can be expanded +authors = [{name = "Seung Hyun Kim", email = "skim0119@gmail.com"}] + +# classifiers can be added here, later +classifiers = [ ] -build-backend = "setuptools.build_meta" -[tool.mypy] -files = "setup.py" -python_version = "3.7" -strict = true -show_error_codes = true -enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] -warn_unreachable = true +[project.urls] +"Homepage" = "https://github.com/GazzolaLab/PyElastica/" +"Bug Reports" = "https://github.com/GazzolaLab/PyElastica/issues" +"Source" = "https://github.com/GazzolaLab/PyElastica/" +"Documentation" = "https://docs.cosseratrods.org/" -[[tool.mypy.overrides]] -module = ["ninja"] -ignore_missing_imports = true +[build-system] +requires = ["scikit-build-core>=0.3.3", "pybind11"] +build-backend = "scikit_build_core.build" +# [tool.mypy] +# files = "setup.py" +# python_version = "3.7" +# strict = true +# show_error_codes = true +# enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] +# warn_unreachable = true +# +# [[tool.mypy.overrides]] +# module = ["ninja"] +# ignore_missing_imports = true [tool.pytest.ini_options] minversion = "6.0" diff --git a/backend/setup.py b/backend/setup.py deleted file mode 100644 index 80240487e..000000000 --- a/backend/setup.py +++ /dev/null @@ -1,139 +0,0 @@ -import os -import re -import subprocess -import sys -from pathlib import Path - -from setuptools import Extension, setup -from setuptools.command.build_ext import build_ext - -# Convert distutils Windows platform specifiers to CMake -A arguments -PLAT_TO_CMAKE = { - "win32": "Win32", - "win-amd64": "x64", - "win-arm32": "ARM", - "win-arm64": "ARM64", -} - - -# A CMakeExtension needs a sourcedir instead of a file list. -# The name must be the _single_ output extension from the CMake build. -# If you need multiple extensions, see scikit-build. -class CMakeExtension(Extension): - def __init__(self, name: str, sourcedir: str = "") -> None: - super().__init__(name, sources=[]) - self.sourcedir = os.fspath(Path(sourcedir).resolve()) - - -class CMakeBuild(build_ext): - def build_extension(self, ext: CMakeExtension) -> None: - # Must be in this form due to bug in .resolve() only fixed in Python 3.10+ - ext_fullpath = Path.cwd() / self.get_ext_fullpath(ext.name) - extdir = ext_fullpath.parent.resolve() - - # Using this requires trailing slash for auto-detection & inclusion of - # auxiliary "native" libs - - debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug - cfg = "Debug" if debug else "Release" - - # CMake lets you override the generator - we need to check this. - # Can be set with Conda-Build, for example. - cmake_generator = os.environ.get("CMAKE_GENERATOR", "") - - # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON - # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code - # from Python. - cmake_args = [ - f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", - f"-DPYTHON_EXECUTABLE={sys.executable}", - f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm - ] - build_args = [] - # Adding CMake arguments set as environment variable - # (needed e.g. to build for ARM OSx on conda-forge) - if "CMAKE_ARGS" in os.environ: - cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item] - - # In this example, we pass in the version to C++. You might not need to. - cmake_args += [f"-DEXAMPLE_VERSION_INFO={self.distribution.get_version()}"] - - if self.compiler.compiler_type != "msvc": - # Using Ninja-build since it a) is available as a wheel and b) - # multithreads automatically. MSVC would require all variables be - # exported for Ninja to pick it up, which is a little tricky to do. - # Users can override the generator with CMAKE_GENERATOR in CMake - # 3.15+. - if not cmake_generator or cmake_generator == "Ninja": - try: - import ninja - - ninja_executable_path = Path(ninja.BIN_DIR) / "ninja" - cmake_args += [ - "-GNinja", - f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}", - ] - except ImportError: - pass - - else: - # Single config generators are handled "normally" - single_config = any(x in cmake_generator for x in {"NMake", "Ninja"}) - - # CMake allows an arch-in-generator style for backward compatibility - contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) - - # Specify the arch if using MSVC generator, but only if it doesn't - # contain a backward-compatibility arch spec already in the - # generator name. - if not single_config and not contains_arch: - cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] - - # Multi-config generators have a different way to specify configs - if not single_config: - cmake_args += [ - f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}" - ] - build_args += ["--config", cfg] - - if sys.platform.startswith("darwin"): - # Cross-compile support for macOS - respect ARCHFLAGS if set - archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) - if archs: - cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] - - # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level - # across all generators. - if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ: - # self.parallel is a Python 3 only way to set parallel jobs by hand - # using -j in the build_ext call, not supported by pip or PyPA-build. - if hasattr(self, "parallel") and self.parallel: - # CMake 3.12+ only. - build_args += [f"-j{self.parallel}"] - - build_temp = Path(self.build_temp) / ext.name - if not build_temp.exists(): - build_temp.mkdir(parents=True) - - subprocess.run( - ["cmake", ext.sourcedir, *cmake_args], cwd=build_temp, check=True - ) - subprocess.run( - ["cmake", "--build", ".", *build_args], cwd=build_temp, check=True - ) - - -# The information here can also be placed in setup.cfg - better separation of -# logic and declaration, and simpler if you include description/version in a file. -setup( - name="elasticapp", - version="0.0.3", - author="Seung Hyun Kim", - author_email="skim0119@gmail.com", - description="A CPP accelerated backend for PyElastica kernels", - long_description="", - ext_modules=[CMakeExtension("elasticapp")], - cmdclass={"build_ext": CMakeBuild}, - zip_safe=False, - python_requires=">=3.10", -) From 060f94f2b7bcb207787d28732a46da8bd93da0c6 Mon Sep 17 00:00:00 2001 From: Ankith Date: Thu, 30 May 2024 15:33:50 +0530 Subject: [PATCH 005/121] Add basic pybind11 example with meson buildsystem --- backend/meson.build | 25 +++++++++++++++++++++ backend/pyproject.toml | 4 ++-- backend/src/example_1.cpp | 46 +++++++++++++++++++++++++++++++++++++++ backend/src/meson.build | 8 +++++++ 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 backend/meson.build create mode 100644 backend/src/example_1.cpp create mode 100644 backend/src/meson.build diff --git a/backend/meson.build b/backend/meson.build new file mode 100644 index 000000000..62c509157 --- /dev/null +++ b/backend/meson.build @@ -0,0 +1,25 @@ +project( + 'elasticapp', + ['cpp'], + version: '0.0.3', + default_options: ['cpp_std=c++17'], +) + +package = 'elasticapp' + +cc = meson.get_compiler('cpp') + +py = import('python').find_installation(pure: false) +py_dep = py.dependency() + +# find dependencies and create dep objects +pybind11_dep = declare_dependency( + include_directories: run_command( + py, + '-c', + 'import pybind11; print(pybind11.get_include());', + check: true, + ).stdout().strip(), +) + +subdir('src') diff --git a/backend/pyproject.toml b/backend/pyproject.toml index a7620e73c..f10127b1b 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -20,8 +20,8 @@ classifiers = [ "Documentation" = "https://docs.cosseratrods.org/" [build-system] -requires = ["scikit-build-core>=0.3.3", "pybind11"] -build-backend = "scikit_build_core.build" +requires = ["meson-python", "pybind11"] +build-backend = "mesonpy" # [tool.mypy] # files = "setup.py" diff --git a/backend/src/example_1.cpp b/backend/src/example_1.cpp new file mode 100644 index 000000000..088cc43c6 --- /dev/null +++ b/backend/src/example_1.cpp @@ -0,0 +1,46 @@ +// Example taken from: https://github.com/pybind/python_example/blob/master/src/main.cpp +// To demonstate the workability of the meson-based builder + +#include + +#define STRINGIFY(x) #x +#define MACRO_STRINGIFY(x) STRINGIFY(x) + +int add(int i, int j) { + return i + j; +} + +namespace py = pybind11; + +PYBIND11_MODULE(example_1, m) { + m.doc() = R"pbdoc( + Pybind11 example plugin + ----------------------- + + .. currentmodule:: python_example + + .. autosummary:: + :toctree: _generate + + add + subtract + )pbdoc"; + + m.def("add", &add, R"pbdoc( + Add two numbers + + Some other explanation about the add function. + )pbdoc"); + + m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( + Subtract two numbers + + Some other explanation about the subtract function. + )pbdoc"); + +#ifdef VERSION_INFO + m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); +#else + m.attr("__version__") = "dev"; +#endif +} \ No newline at end of file diff --git a/backend/src/meson.build b/backend/src/meson.build new file mode 100644 index 000000000..1668a738e --- /dev/null +++ b/backend/src/meson.build @@ -0,0 +1,8 @@ +example_1 = py.extension_module( + 'example_1', + sources: ['example_1.cpp'], + dependencies: [py_dep, pybind11_dep], + link_language: 'cpp', + install: true, + subdir: package, +) From a587b6535cd18d66a0e6a7bba51d05bc1040a5c1 Mon Sep 17 00:00:00 2001 From: Ankith Date: Sun, 9 Jun 2024 23:44:04 +0530 Subject: [PATCH 006/121] Start integrating dependencies - brigand and cxxopts resolved via meson wraps - blaze, blaze_tensor and sleef are installed via a custom shell script that's called from the meson build --- .gitignore | 3 ++ backend/.gitignore | 10 ++++ backend/buildscripts/build-all.sh | 70 +++++++++++++++++++++++++++ backend/meson.build | 42 ++++++++++++++++ backend/subprojects/blaze.wrap | 8 +++ backend/subprojects/blaze_tensor.wrap | 7 +++ backend/subprojects/brigand.wrap | 8 +++ backend/subprojects/cxxopts.wrap | 14 ++++++ backend/subprojects/sleef.wrap | 8 +++ backend/subprojects/tbb.wrap | 8 +++ backend/subprojects/yaml-cpp.wrap | 8 +++ 11 files changed, 186 insertions(+) create mode 100644 backend/buildscripts/build-all.sh create mode 100644 backend/subprojects/blaze.wrap create mode 100644 backend/subprojects/blaze_tensor.wrap create mode 100644 backend/subprojects/brigand.wrap create mode 100644 backend/subprojects/cxxopts.wrap create mode 100644 backend/subprojects/sleef.wrap create mode 100644 backend/subprojects/tbb.wrap create mode 100644 backend/subprojects/yaml-cpp.wrap diff --git a/.gitignore b/.gitignore index 535b8659c..e799563d2 100644 --- a/.gitignore +++ b/.gitignore @@ -236,3 +236,6 @@ outcmaes/* # csv files *.csv + +# ./backend dependencies +deps diff --git a/backend/.gitignore b/backend/.gitignore index 46f42f8f3..9ac281d62 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -9,3 +9,13 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake _deps + +subprojects/packagecache + +subprojects/blaze +subprojects/blaze_tensor +subprojects/brigand +subprojects/cxxopts-3.1.1 +subprojects/sleef +subprojects/tbb +subprojects/yaml-cpp diff --git a/backend/buildscripts/build-all.sh b/backend/buildscripts/build-all.sh new file mode 100644 index 000000000..7ae68cab9 --- /dev/null +++ b/backend/buildscripts/build-all.sh @@ -0,0 +1,70 @@ + +set -e -x + +OLD_CWD=$(pwd) + +export ELASTICA_DEP_PREFIX="$OLD_CWD/../deps" + +mkdir -p $ELASTICA_DEP_PREFIX && cd $ELASTICA_DEP_PREFIX + +export ELASTICA_INSTALL_PREFIX="$ELASTICA_DEP_PREFIX/installed" + +# With this we +# 1) Force install prefix to $ELASTICA_INSTALL_PREFIX +# 2) use lib directory within $ELASTICA_INSTALL_PREFIX (and not lib64) +# 3) make release binaries +# 4) build shared libraries +# 5) not have @rpath in the linked dylibs (needed on macs only) +# 6) tell cmake to search in $ELASTICA_INSTALL_PREFIX for sub dependencies +export ELASTICA_BASE_CMAKE_FLAGS="-DCMAKE_INSTALL_PREFIX=$ELASTICA_INSTALL_PREFIX \ + -DCMAKE_INSTALL_LIBDIR:PATH=lib \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=true \ + -DCMAKE_INSTALL_NAME_DIR=$ELASTICA_INSTALL_PREFIX/lib \ + -DCMAKE_PREFIX_PATH=$ELASTICA_INSTALL_PREFIX" + +mkdir -p $ELASTICA_INSTALL_PREFIX + +# set pkg_config_path so that system can find dependencies +export PKG_CONFIG_PATH="$ELASTICA_INSTALL_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH" + +# blaze +if [ ! -d blaze ]; then + git clone --depth 1 https://bitbucket.org/blaze-lib/blaze.git && cd blaze + + mkdir build + cmake -B build $ELASTICA_BASE_CMAKE_FLAGS \ + -DBLAZE_SHARED_MEMORY_PARALLELIZATION=OFF \ + -DUSE_LAPACK=OFF \ + -S . + cmake --build build --parallel $(nproc) + cmake --install build + cd .. +fi + +# blaze tensor +if [ ! -d blaze_tensor ]; then + git clone --depth 1 https://github.com/STEllAR-GROUP/blaze_tensor.git && cd blaze_tensor + + mkdir build + cmake -B build $ELASTICA_BASE_CMAKE_FLAGS \ + -Dblaze_DIR="$ELASTICA_INSTALL_PREFIX/share/blaze/cmake/" \ + -S . + cmake --build build --parallel $(nproc) + cmake --install build + cd .. +fi + +# sleef +if [ ! -d sleef ]; then + git clone --depth 1 https://github.com/shibatch/sleef && cd sleef + + mkdir build + cmake -B build $ELASTICA_BASE_CMAKE_FLAGS \ + -S . + cmake --build build --parallel $(nproc) + cmake --install build + cd .. +fi + +cd $OLD_CWD \ No newline at end of file diff --git a/backend/meson.build b/backend/meson.build index 62c509157..e9a1d4650 100644 --- a/backend/meson.build +++ b/backend/meson.build @@ -22,4 +22,46 @@ pybind11_dep = declare_dependency( ).stdout().strip(), ) +fs = import('fs') + +message('Running buildscripts/build-all.sh (might take a while)') +buildscripts = files('buildscripts/build-all.sh') +res = run_command('bash', buildscripts, check: false) +message(res.stdout().strip()) +if res.returncode() != 0 + error(res.stderr().strip()) +endif + +deps_installed = fs.parent(meson.current_source_dir()) / 'deps' / 'installed' + +deps_inc_dirs = [deps_installed / 'include'] +deps_lib_dirs = [deps_installed / 'lib'] + +# see https://github.com/mesonbuild/meson/issues/7943 +# The strategy for the below required dependencies are as follows +# - First, meson tries to resolve the dependency by searching on the system +# - if it doesn't find it, it fallbacks to the subproject wrap system or the +# ones installed in ./buildscripts + +blaze_dep = dependency('blaze') +blaze_tensor_dep = dependency('BlazeTensor') +sleef_dep = dependency('sleef', required: false) +if not sleef_dep.found() + sleef_dep = declare_dependency( + include_directories: deps_inc_dirs, + dependencies: cc.find_library('sleef', dirs: deps_lib_dirs), + ) +endif + +brigand_dep = dependency('brigand', fallback : 'brigand') +cxxopts_dep = dependency('cxxopts', fallback : 'cxxopts') + +# meson-python: error: Could not map installation path to an equivalent wheel directory: +# tbb_dep = dependency('tbb', fallback : 'tbb') +# yaml_cpp_dep = dependency('yaml-cpp', fallback : 'yaml-cpp') + +# TODO: are these required? +# setup_library "HighFive" "https://github.com/BlueBrain/HighFive" +# setup_library "spline" "https://github.com/tp5uiuc/spline.git" + subdir('src') diff --git a/backend/subprojects/blaze.wrap b/backend/subprojects/blaze.wrap new file mode 100644 index 000000000..f7d4cf05f --- /dev/null +++ b/backend/subprojects/blaze.wrap @@ -0,0 +1,8 @@ +[wrap-git] +url = https://bitbucket.org/blaze-lib/blaze.git +revision = head +depth = 1 +method = cmake + +[provide] +blaze = blaze_dep diff --git a/backend/subprojects/blaze_tensor.wrap b/backend/subprojects/blaze_tensor.wrap new file mode 100644 index 000000000..127baae0c --- /dev/null +++ b/backend/subprojects/blaze_tensor.wrap @@ -0,0 +1,7 @@ +[wrap-git] +url = https://github.com/STEllAR-GROUP/blaze_tensor.git +revision = head +method = cmake + +[provide] +BlazeTensor = BlazeTensor_dep diff --git a/backend/subprojects/brigand.wrap b/backend/subprojects/brigand.wrap new file mode 100644 index 000000000..d24c3f31a --- /dev/null +++ b/backend/subprojects/brigand.wrap @@ -0,0 +1,8 @@ +[wrap-git] +url = https://github.com/edouarda/brigand.git +revision = head +depth = 1 +method = cmake + +[provide] +brigand = brigand_dep diff --git a/backend/subprojects/cxxopts.wrap b/backend/subprojects/cxxopts.wrap new file mode 100644 index 000000000..d2c9d1b1c --- /dev/null +++ b/backend/subprojects/cxxopts.wrap @@ -0,0 +1,14 @@ +# Taken from meson wrap db +[wrap-file] +directory = cxxopts-3.1.1 +source_url = https://github.com/jarro2783/cxxopts/archive/v3.1.1.tar.gz +source_filename = cxxopts-3.1.1.tar.gz +source_hash = 523175f792eb0ff04f9e653c90746c12655f10cb70f1d5e6d6d9491420298a08 +patch_filename = cxxopts_3.1.1-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/cxxopts_3.1.1-1/get_patch +patch_hash = 3a38f72d4a9c394d32db47bbca6f00cb6ee330434b82b653f9d04bdea73170bb +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/cxxopts_3.1.1-1/cxxopts-3.1.1.tar.gz +wrapdb_version = 3.1.1-1 + +[provide] +cxxopts = cxxopts_dep diff --git a/backend/subprojects/sleef.wrap b/backend/subprojects/sleef.wrap new file mode 100644 index 000000000..6281636c9 --- /dev/null +++ b/backend/subprojects/sleef.wrap @@ -0,0 +1,8 @@ +[wrap-git] +url = https://github.com/shibatch/sleef.git +revision = head +depth = 1 +method = cmake + +[provide] +sleef = sleef_dep diff --git a/backend/subprojects/tbb.wrap b/backend/subprojects/tbb.wrap new file mode 100644 index 000000000..f0ee601d7 --- /dev/null +++ b/backend/subprojects/tbb.wrap @@ -0,0 +1,8 @@ +[wrap-git] +url = https://github.com/oneapi-src/oneTBB.git +revision = head +depth = 1 +method = cmake + +[provide] +tbb = tbb_dep diff --git a/backend/subprojects/yaml-cpp.wrap b/backend/subprojects/yaml-cpp.wrap new file mode 100644 index 000000000..b1b886c6a --- /dev/null +++ b/backend/subprojects/yaml-cpp.wrap @@ -0,0 +1,8 @@ +[wrap-git] +url = https://github.com/jbeder/yaml-cpp.git +revision = head +depth = 1 +method = cmake + +[provide] +yaml-cpp = yaml_cpp_dep From 8c3658477f9df3c93d90403b553394d3e13d9863 Mon Sep 17 00:00:00 2001 From: Ankith Date: Thu, 13 Jun 2024 17:07:22 +0530 Subject: [PATCH 007/121] elasticapp _batch_matmul ports with benchmarking --- backend/benchmarking/matmul.py | 42 +++++++++++++ backend/src/_linalg.cpp | 107 +++++++++++++++++++++++++++++++++ backend/src/meson.build | 9 +++ 3 files changed, 158 insertions(+) create mode 100644 backend/benchmarking/matmul.py create mode 100644 backend/src/_linalg.cpp diff --git a/backend/benchmarking/matmul.py b/backend/benchmarking/matmul.py new file mode 100644 index 000000000..3235e0ef6 --- /dev/null +++ b/backend/benchmarking/matmul.py @@ -0,0 +1,42 @@ +from elasticapp._linalg import batch_matmul_naive, batch_matmul_blaze +from elastica._linalg import _batch_matmul +import numpy +import time + +# warm up jit for fair comparison +random_1 = numpy.random.random((3, 3, 1)) +random_2 = numpy.random.random((3, 3, 1)) +out1 = _batch_matmul(random_1, random_2) + + +def benchmark_batchsize(funcs: list, batches: list[int], num_iterations: int = 1000): + ret: dict = {} + for batch_size in batches: + random_a = numpy.random.random((3, 3, batch_size)) + random_b = numpy.random.random((3, 3, batch_size)) + + ret[batch_size] = {} + for func in funcs: + start = time.perf_counter() + for _ in range(num_iterations): + func(random_a, random_b) + + ret[batch_size][func.__name__] = ( + time.perf_counter() - start + ) / num_iterations + + return ret + + +results = benchmark_batchsize( + [batch_matmul_naive, batch_matmul_blaze, _batch_matmul], [2**i for i in range(14)] +) +for size, data in results.items(): + pyelastica = data["_batch_matmul"] + elasticapp = data["batch_matmul_naive"] + elasticapp_blaze = data["batch_matmul_blaze"] + print(f"{size = }") + print(f"{pyelastica = }") + print(f"{elasticapp = }, ratio: {elasticapp / pyelastica}") + print(f"{elasticapp_blaze = }, ratio: {elasticapp_blaze / pyelastica}") + print() diff --git a/backend/src/_linalg.cpp b/backend/src/_linalg.cpp new file mode 100644 index 000000000..b628ebd2a --- /dev/null +++ b/backend/src/_linalg.cpp @@ -0,0 +1,107 @@ +#include +#include +#include + +namespace py = pybind11; + +using blaze::StaticMatrix; + +/* Simple rewrite of elastica._linalg._batch_matmul */ +py::array_t batch_matmul_naive( + py::array_t first_matrix_collection, + py::array_t second_matrix_collection) +{ + auto s1 = first_matrix_collection.shape(0); + auto s2 = first_matrix_collection.shape(1); + auto max_k = first_matrix_collection.shape(2); + auto output_matrix = py::array_t{{s1, s2, max_k}}; + + auto a = first_matrix_collection.unchecked<3>(); + auto b = second_matrix_collection.unchecked<3>(); + auto c = output_matrix.mutable_unchecked<3>(); + for (py::ssize_t i = 0; i < 3; i++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + for (py::ssize_t m = 0; m < 3; m++) + { + for (py::ssize_t k = 0; k < max_k; k++) + { + c(i, m, k) += a(i, j, k) * b(j, m, k); + } + } + } + } + return output_matrix; +} + +/* blaze implementation of elastica._linalg._batch_matmul */ +py::array_t batch_matmul_blaze( + py::array_t first_matrix_collection, + py::array_t second_matrix_collection) +{ + auto s1 = first_matrix_collection.shape(0); + auto s2 = first_matrix_collection.shape(1); + auto max_k = first_matrix_collection.shape(2); + auto output_matrix = py::array_t{{s1, s2, max_k}}; + + auto a = first_matrix_collection.unchecked<3>(); + auto b = second_matrix_collection.unchecked<3>(); + auto c = output_matrix.mutable_unchecked<3>(); + + StaticMatrix blaze_a; + StaticMatrix blaze_b; + StaticMatrix blaze_c; + for (py::ssize_t k = 0; k < max_k; k++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + blaze_a(i, j) = a(i, j, k); + blaze_b(i, j) = b(i, j, k); + } + } + blaze_c = blaze_a * blaze_b; + for (py::ssize_t i = 0; i < 3; i++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + c(i, j, k) = blaze_c(i, j); + } + } + } + + return output_matrix; +} + +PYBIND11_MODULE(_linalg, m) +{ + m.doc() = R"pbdoc( + elasticapp _linalg + --------------- + + .. currentmodule:: _linalg + + .. autosummary:: + :toctree: _generate + + batch_matmul_naive + )pbdoc"; + + m.def("batch_matmul_naive", &batch_matmul_naive, R"pbdoc( + This is batch matrix matrix multiplication function. Only batch + of 3x3 matrices can be multiplied. + )pbdoc"); + + m.def("batch_matmul_blaze", &batch_matmul_blaze, R"pbdoc( + This is batch matrix matrix multiplication function. Only batch + of 3x3 matrices can be multiplied. + )pbdoc"); + +#ifdef VERSION_INFO + m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); +#else + m.attr("__version__") = "dev"; +#endif +} \ No newline at end of file diff --git a/backend/src/meson.build b/backend/src/meson.build index 1668a738e..9fb245448 100644 --- a/backend/src/meson.build +++ b/backend/src/meson.build @@ -6,3 +6,12 @@ example_1 = py.extension_module( install: true, subdir: package, ) + +_linalg = py.extension_module( + '_linalg', + sources: ['_linalg.cpp'], + dependencies: [py_dep, pybind11_dep, blaze_dep], + link_language: 'cpp', + install: true, + subdir: package, +) From 3e05a9e4bfe4270d37abc409c92723b77b1af2f8 Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Fri, 14 Jun 2024 18:14:45 +0900 Subject: [PATCH 008/121] add math utility libraries --- backend/src/CMakeLists.txt | 94 +++++++ backend/src/Utilities/CMakeLists.txt | 109 ++++++++ backend/src/Utilities/DefineTypes.h | 12 + backend/src/Utilities/Math.hpp | 15 ++ .../Math/BlazeDetail/BlazeCalculus.hpp | 91 +++++++ .../Math/BlazeDetail/BlazeLinearAlgebra.hpp | 245 ++++++++++++++++++ .../Math/BlazeDetail/BlazeRotation.hpp | 152 +++++++++++ backend/src/Utilities/Math/CMakeLists.txt | 23 ++ backend/src/Utilities/Math/Rot3.hpp | 21 ++ backend/src/Utilities/Math/Types.hpp | 50 ++++ backend/src/Utilities/Math/Vec3.hpp | 21 ++ 11 files changed, 833 insertions(+) create mode 100644 backend/src/CMakeLists.txt create mode 100644 backend/src/Utilities/CMakeLists.txt create mode 100644 backend/src/Utilities/DefineTypes.h create mode 100644 backend/src/Utilities/Math.hpp create mode 100644 backend/src/Utilities/Math/BlazeDetail/BlazeCalculus.hpp create mode 100644 backend/src/Utilities/Math/BlazeDetail/BlazeLinearAlgebra.hpp create mode 100644 backend/src/Utilities/Math/BlazeDetail/BlazeRotation.hpp create mode 100644 backend/src/Utilities/Math/CMakeLists.txt create mode 100644 backend/src/Utilities/Math/Rot3.hpp create mode 100644 backend/src/Utilities/Math/Types.hpp create mode 100644 backend/src/Utilities/Math/Vec3.hpp diff --git a/backend/src/CMakeLists.txt b/backend/src/CMakeLists.txt new file mode 100644 index 000000000..03136235a --- /dev/null +++ b/backend/src/CMakeLists.txt @@ -0,0 +1,94 @@ +# Distributed under the MIT License. See LICENSE for details. + +#add_subdirectory(DataStructures) +#add_subdirectory(ErrorHandling) +#add_library(${PROJECT_NAME}::ErrorHandling ALIAS ErrorHandling) + +#add_subdirectory(ModuleSettings) +#add_library(${PROJECT_NAME}::ModuleSettings ALIAS ModuleSettings) + +#add_subdirectory(Options) +#add_library(${PROJECT_NAME}::Options ALIAS Options) + +#add_subdirectory(Parallel) +#add_library(${PROJECT_NAME}::Parallel ALIAS Parallel) + +#add_subdirectory(PythonBindings) +#if (${ELASTICA_BUILD_PYTHON_BINDINGS}) +# add_library(${PROJECT_NAME}::PythonBindings ALIAS PythonBindings) +#endif () + +#add_subdirectory(Simulator) +#add_library(${PROJECT_NAME}::Simulator ALIAS Simulator) + +add_subdirectory(Systems) +add_library(${PROJECT_NAME}::Systems ALIAS Systems) + +#add_subdirectory(Time) +#add_library(${PROJECT_NAME}::Time ALIAS Time) + +add_subdirectory(Utilities) +add_library(${PROJECT_NAME}::Utilities ALIAS Utilities) + +add_library(${PROJECT_NAME} INTERFACE) +target_include_directories(${PROJECT_NAME} INTERFACE + $ + $ + $) + +set(ELASTICA_TOPLEVEL_LIBS + "ErrorHandling;ModuleSettings;Options;Parallel;Simulator;Systems;Time;Utilities") + +foreach (ELASTICA_LIB ${ELASTICA_TOPLEVEL_LIBS}) + target_link_libraries(${PROJECT_NAME} + INTERFACE + ${ELASTICA_LIB} + ) +endforeach (ELASTICA_LIB ${ELASTICA_TOPLEVEL_LIBS}) + +# set to global +set_property(GLOBAL PROPERTY ELASTICA_TOPLEVEL_LIBS_PROPERTY ${ELASTICA_TOPLEVEL_LIBS}) +unset(ELASTICA_TOPLEVEL_LIBS) + +# target_link_libraries(${PROJECT_NAME} INTERFACE libs) + +# In case each library needs to be exported separately +# +# In case each library needs to be exported separately +# foreach (lib ${libs}) +# target_include_directories(${lib} +# $ +# $ +# $) +# endforeach (lib ${libs}) + +#set_property(TARGET ElasticaFlags +# APPEND PROPERTY +# INTERFACE_COMPILE_OPTIONS +# $<$:-stdlib=libc++>) +# +#check_and_add_cxx_link_flag(-stdlib=libc++) +#check_and_add_cxx_link_flag(-libc++experimental) +#check_and_add_cxx_link_flag(-stdlib=libstdc++) + +#target_link_libraries(elastica +# PUBLIC +# Simulator +# Environments +# Utilities +# ErrorHandling +# IO +# Systems +# ElasticaFlags +# ) + +#message(${ElasticaFlags}) +#if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") +# message("Bitch") +# set(CXX_FILESYSTEM_LIBRARIES "stdc++fs") +# target_link_libraries(elastica PUBLIC ${CXX_FILESYSTEM_LIBRARIES}) +#elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") +# set(CXX_FILESYSTEM_LIBRARIES "") +## set(CXX_FILESYSTEM_LIBRARIES "c++experimental") +#endif () +#target_link_libraries(elastica PUBLIC ${CXX_FILESYSTEM_LIBRARIES}) diff --git a/backend/src/Utilities/CMakeLists.txt b/backend/src/Utilities/CMakeLists.txt new file mode 100644 index 000000000..b9bcaad64 --- /dev/null +++ b/backend/src/Utilities/CMakeLists.txt @@ -0,0 +1,109 @@ +# Distributed under the MIT License. See LICENSE.txt for details. + +set(LIBRARY Utilities) + +add_elastica_library(${LIBRARY}) + +#add_subdirectory(Geometry) +#add_subdirectory(Logging) +add_subdirectory(Math) +#add_subdirectory(MemoryMeter) +#add_subdirectory(NamedType) +#add_subdirectory(Singleton) +#add_subdirectory(Timing) +#add_subdirectory(TypeTraits) +#add_subdirectory(ValueTraits) + +# elastica_target_sources( +# ${LIBRARY} +# PRIVATE +# Demangle.cpp +# SystemClock.cpp +# PrettyType.cpp +# WrapText.cpp +# WidthStream.cpp +# ) + +elastica_target_headers( + ${LIBRARY} + INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/elastica + HEADERS + # Approx.hpp + # AsConst.hpp + # Byte.hpp + # Clamp.hpp + # CountingIterator.hpp + # CRTP.hpp + DefineTypes.h + # Demangle.hpp + # Defer.hpp + # End.hpp + # FakeVirtual.hpp + # ForceInline.hpp + # Geometry.hpp + # Get.hpp + # Generators.hpp + # GSL.hpp + # IgnoreUnused.hpp + # Invoke.hpp + # KarmarkarKarpPartition.hpp + # ListRequires.hpp + # Logging.hpp + # MakeArray.hpp + # MakeCopyable.hpp + # MakeFromTuple.hpp + # MakeNamedFunctor.hpp + # MakeSignalingNan.hpp + # MakeString.hpp + Math.hpp + # MemoryMeter.hpp + # MemoryPool.hpp + # NamedTemplate.hpp + # NamedType.hpp + # NumericAt.hpp + # NumericRange.hpp + # NonCopyable.hpp + # NonCreatable.hpp + # NoSuchType.hpp + # Overloader.hpp + # PrettyType.hpp + # PrintHelpers.hpp + # ProtocolHelpers.hpp + # Rampup.hpp + # Registration.hpp + # Requires.hpp + # Singleton.hpp + # StaticConst.hpp + # StaticWarning.hpp + # StdHelpers.hpp + # StdVectorHelpers.hpp + # SystemClock.hpp + # TaggedTuple.hpp + # Thresholds.hpp + # TimeStamps.h + # Timing.h + # TMPL.hpp + # TMPLDebugging.hpp + # TypeTraits.hpp + # Unroll.hpp + # WidthStream.hpp + # WrapText.hpp +) + +target_link_libraries( + ${LIBRARY} + PUBLIC + Blaze + Brigand + ErrorHandling +) + +#set(LIBRARY_SOURCES Utilities.cpp ) +# +#add_elastica_library(${LIBRARY} ${LIBRARY_SOURCES}) +# +#target_link_libraries(${LIBRARY} INTERFACE ErrorHandling) +# + +# add_subdirectory(AppSupport) +# add_subdirectory(ConvertCase) diff --git a/backend/src/Utilities/DefineTypes.h b/backend/src/Utilities/DefineTypes.h new file mode 100644 index 000000000..bd9a7555e --- /dev/null +++ b/backend/src/Utilities/DefineTypes.h @@ -0,0 +1,12 @@ +#pragma once +#include + +namespace elastica { + + using real_t = double; +// typedef REAL real; +#define ALIGNMENT 32 + + using id_t = uint64_t; + +} // namespace elastica diff --git a/backend/src/Utilities/Math.hpp b/backend/src/Utilities/Math.hpp new file mode 100644 index 000000000..698ba17c5 --- /dev/null +++ b/backend/src/Utilities/Math.hpp @@ -0,0 +1,15 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +// Free operations +// #include "Math/Invert.hpp" +// #include "Math/Square.hpp" +// #include "Math/SqrLength.hpp" + +// Containers +#include "Math/Vec3.hpp" + +//#include diff --git a/backend/src/Utilities/Math/BlazeDetail/BlazeCalculus.hpp b/backend/src/Utilities/Math/BlazeDetail/BlazeCalculus.hpp new file mode 100644 index 000000000..07cc5404b --- /dev/null +++ b/backend/src/Utilities/Math/BlazeDetail/BlazeCalculus.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include "blaze/Blaze.h" +#include "blaze_tensor/Blaze.h" + +namespace elastica{ + //************************************************************************** + /*!\brief Simple 2-point difference rule with zero at end points. + // + // \details + // Discrete 2-point difference in \elastica of a function f:[a,b]-> R, i.e + // D f[a,b] -> df[a,b] where f satisfies the conditions + // f(a) = f(b) = 0.0. Operates from rod's elemental space to nodal space. + // + // \example + // The following shows a typical use of the difference kernel + // with the expected (correct) result also shown. + // \snippet test_calculus.cpp difference_kernel_example + // + // \param[out] out_matrix(3, n_nodes) difference values + // \param[in] in_matrix(3, n_elems) vector batch \n + // where n_nodes = n_elems + 1 + // + // \return void/None + // + // \see fill later? + */ + template // blaze Matrix expression type 2 + void two_point_difference_kernel(MT1& out_matrix, const MT2& in_matrix) { + constexpr std::size_t dimension(3UL); + assert(in_matrix.rows() == dimension); + assert(out_matrix.rows() == dimension); + const std::size_t n_elems = in_matrix.columns(); + const std::size_t n_nodes = n_elems + 1UL; + //assert(out_matrix.columns() == n_nodes); + + blaze::column(out_matrix, 0UL) = blaze::column(in_matrix, 0UL); + blaze::column(out_matrix, n_nodes - 1UL) = + -blaze::column(in_matrix, n_elems - 1UL); + blaze::submatrix(out_matrix, 0UL, 1UL, dimension, n_elems - 1UL) = + blaze::submatrix(in_matrix, 0UL, 1UL, dimension, n_elems - 1UL) - + blaze::submatrix(in_matrix, 0UL, 0UL, dimension, n_elems - 1UL); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Simple trapezoidal quadrature rule with zero at end points. + // + // \details + // Discrete integral of a function in \elastica + // \f$ : [a,b] \rightarrow \mathbf{R}, \int_{a}^{b} f \rightarrow \mathbf{R} \f$ + // where f satisfies the conditions f(a) = f(b) = 0.0. + // Operates from rod's elemental space to nodal space. + // + // \example + // The following shows a typical use of the quadrature kernel + // with the expected (correct) result also shown. + // \snippet test_calculus.cpp quadrature_kernel_example + // + // \param[out] out_matrix(3, n_nodes) quadrature values + // \param[in] in_matrix(3, n_elems) vector batch \n + // where n_nodes = n_elems + 1 + // + // \return void/None + // + // \see fill later? + */ + template // blaze Matrix expression type 2 + void quadrature_kernel(MT1& out_matrix, const MT2& in_matrix) { + constexpr std::size_t dimension(3UL); + const std::size_t n_elems = in_matrix.columns(); + assert(in_matrix.rows() == dimension); + assert(out_matrix.rows() == dimension); + const std::size_t n_nodes = n_elems + 1UL; + assert(out_matrix.columns() == n_nodes); + using ValueType = typename MT1::ElementType; + + blaze::column(out_matrix, 0UL) = + ValueType(0.5) * blaze::column(in_matrix, 0UL); + blaze::column(out_matrix, n_nodes - 1UL) = + ValueType(0.5) * blaze::column(in_matrix, n_elems - 1UL); + blaze::submatrix(out_matrix, 0UL, 1UL, dimension, n_elems - 1UL) = + ValueType(0.5) * + (blaze::submatrix(in_matrix, 0UL, 1UL, dimension, n_elems - 1UL) + + blaze::submatrix(in_matrix, 0UL, 0UL, dimension, n_elems - 1UL)); + } + //************************************************************************** + +} // namespace elastica diff --git a/backend/src/Utilities/Math/BlazeDetail/BlazeLinearAlgebra.hpp b/backend/src/Utilities/Math/BlazeDetail/BlazeLinearAlgebra.hpp new file mode 100644 index 000000000..1c3060573 --- /dev/null +++ b/backend/src/Utilities/Math/BlazeDetail/BlazeLinearAlgebra.hpp @@ -0,0 +1,245 @@ +#pragma once + +#include "blaze/Blaze.h" +#include "blaze_tensor/Blaze.h" + +/* some of them have for loops until blaze_tensor comes up with 3D tensor */ +/* products imp */ +/* things would have been much simpler if there was einsum() in blaze */ +/* NOTE: % operator corresponds to Schur product for matrices and tensors */ +namespace elastica { + //************************************************************************** + /*!\brief Vector Diference + // + // \param[out] output_vector(3, n_elems) + // \param[in] input_vector(3, n_elems) + // + // \return void/None + // + // \see TODO: fill later + */ + template + inline auto difference_kernel(const V& in_vector){ + constexpr std::size_t dimension(3UL); + assert(in_vector.rows() == dimension); + const std::size_t n_nodes = in_vector.columns(); + const std::size_t n_elems = n_nodes - 1UL; + + return blaze::submatrix(in_vector, 0UL, 1UL, dimension, n_elems) - + blaze::submatrix(in_vector, 0UL, 0UL, dimension, n_elems); + } + + //************************************************************************** + /*!\brief Batchwise matrix-vector product. + // + // \details + // Computes a batchwise matrix-vector product given in indical notation: \n + // matvec_batch{ik} = matrix_batch{ijk} * vector_batch{jk} + // + // \example + // The following shows a typical use of the batch_matvec function + // with the expected (correct) result also shown. + // \snippet test_linalg.cpp batch_matvec_example + // + // \param[out] matvec_batch(3, n_elems) matrix-vector product + // \param[in] matrix_batch(3, 3, n_elems) + // \param[in] vector_batch(3, n_elems) + // + // \return void/None + // + // \see fill later? + */ + template // blaze Matrix expression type 2 + void batch_matvec(MT1& matvec_batch, + const TT& matrix_batch, + const MT2& vector_batch) { + constexpr std::size_t dimension(3UL); + //const std::size_t n_elems = matrix_batch.columns(); + + // Written for debugging purpose +// const std::size_t vbatch_columns = vector_batch.columns(); +// const std::size_t vbatch_rows = vector_batch.rows(); +// const std::size_t mbatch_pages = matrix_batch.pages(); +// const std::size_t mbatch_columns = matrix_batch.columns(); +// const std::size_t mbatch_rows = matrix_batch.rows(); +// +// const std::size_t size1 = blaze::pageslice(matrix_batch, 0UL).columns(); +// const std::size_t size0 = blaze::pageslice(matrix_batch, 0UL).rows(); + // assert(matvec_batch.columns() == n_elems); + // assert(matvec_batch.rows() == dimension); + // assert(matrix_batch.pages() == dimension); + // assert(matrix_batch.rows() == dimension); + // assert(vector_batch.columns() == n_elems); + // assert(vector_batch.rows() == dimension); + + for (std::size_t i(0UL); i < dimension; ++i) { + auto val1 = blaze::pageslice(matrix_batch, i); + auto val2 = blaze::sum(val1 % vector_batch); + blaze::row(matvec_batch, i) = val2; + } + } + //************************************************************************** + + //************************************************************************** + /*!\brief Batchwise matrix-matrix product. + // + // \details + // Computes a batchwise matrix-matrix product given in + // indical notation: \n + // matmul_batch{ilk} = first_matrix_batch{ijk} * second_matrix_batch{jlk} + // + // \example + // The following shows a typical use of the batch_matmul function + // with the expected (correct) result also shown. + // \snippet test_linalg.cpp batch_matmul_example + // + // \param[out] matmul_batch(3, 3, n_elems) matrix-matrix product + // \param[in] first_matrix_batch(3, 3, n_elems) + // \param[in] second_matrix_batch(3, 3, n_elems) + // + // \return void/None + // + // \see fill later? + */ + template // blaze Tensor expression type 3 + void batch_matmul(TT1& matmul_batch, + const TT2& first_matrix_batch, + const TT3& second_matrix_batch) { + constexpr std::size_t dimension(3UL); + const std::size_t n_elems = first_matrix_batch.columns(); + assert(matmul_batch.pages() == dimension); + assert(matmul_batch.rows() == dimension); + assert(matmul_batch.columns() == n_elems); + assert(first_matrix_batch.pages() == dimension); + assert(first_matrix_batch.rows() == dimension); + assert(second_matrix_batch.pages() == dimension); + assert(second_matrix_batch.rows() == dimension); + assert(second_matrix_batch.columns() == n_elems); + + for (std::size_t i(0UL); i < dimension; ++i) + for (std::size_t j(0UL); j < dimension; ++j) + /* loop over dimensions, lesser iterations, bigger slices */ + blaze::row(blaze::pageslice(matmul_batch, i), j) = + blaze::sum( + blaze::pageslice(first_matrix_batch, i) % + blaze::trans(blaze::rowslice(second_matrix_batch, j))); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Batchwise vector-vector cross product. + // + // \details + // Computes a batchwise vector-vector cross product given in + // indical notation: \n + // cross_batch{il} = LCT{ijk} * first_vector_batch{jl} * + // second_vector_batch{kl} \n + // where LCT is the Levi-Civita Tensor + // + // \example + // The following shows a typical use of the batch_cross function + // with the expected (correct) result also shown. + // \snippet test_linalg.cpp batch_cross_example + // + // \param[out] cross_batch(3, n_elems) vector-vector cross product + // \param[in] first_vector_batch(3, n_elems) + // \param[in] second_vector_batch(3, n_elems) + // + // \return void/None + // + // \see fill later? + */ + template // blaze Matrix expression type 3 + void batch_cross(MT1& cross_batch, + const MT2& first_vector_batch, + const MT3& second_vector_batch) { + constexpr std::size_t dimension(3UL); + //const std::size_t n_elems = first_vector_batch.columns(); + assert(cross_batch.rows() == dimension); + //assert(cross_batch.columns() == n_elems); + assert(first_vector_batch.rows() == dimension); + assert(second_vector_batch.rows() == dimension); + //assert(second_vector_batch.columns() == n_elems); + + for (std::size_t i(0UL); i < dimension; ++i) + /* loop over dimensions, lesser iterations, bigger slices */ + /* remainder operator % cycles the indices across dimension*/ + blaze::row(cross_batch, i) = + blaze::row(first_vector_batch, (i + 1UL) % dimension) * + blaze::row(second_vector_batch, (i + 2UL) % dimension) - + blaze::row(first_vector_batch, (i + 2UL) % dimension) * + blaze::row(second_vector_batch, (i + 1UL) % dimension); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Batchwise vector-vector dot product. + // + // \details + // Computes a batchwise vector-vector dot product given in indical + // notation: \n + // dot_batch{j} = first_vector_batch{ij} * second_vector_batch{ij} + // + // \example + // The following shows a typical use of the batch_dot function + // with the expected (correct) result also shown. + // \snippet test_linalg.cpp batch_dot_example + // + // \param[in] first_vector_batch(3, n_elems) + // \param[in] second_vector_batch(3, n_elems) + // + // \return dot_batch(n_elems): ET object pointer to the dot + // product (for lazy evaluation). + // + // \see fill later? + */ + template // blaze Matrix expression type 2 + auto batch_dot(const MT1& first_vector_batch, + const MT2& second_vector_batch) { + constexpr std::size_t dimension(3UL); + const std::size_t n_elems = first_vector_batch.columns(); + assert(first_vector_batch.rows() == dimension); + assert(second_vector_batch.rows() == dimension); + assert(second_vector_batch.columns() == n_elems); + + return blaze::trans( + blaze::sum(first_vector_batch % second_vector_batch)); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Batchwise vector L2 norm. + // + // \details + // Computes a batchwise vector L2 norm given in indical notation: \n + // norm_batch{j} = (vector_batch{ij} * vector_batch{ij})^0.5 + // + // \example + // The following shows a typical use of the batch_norm function + // with the expected (correct) result also shown. + // \snippet test_linalg.cpp batch_norm_example + // + // \param[in] vector_batch(3, n_elems) + // + // \return norm_batch(n_elems): ET object pointer to the vector L2 norm + // (for lazy evaluation). + // + // \see fill later? + */ + template // blaze vector expression type + auto batch_norm(const MT& vector_batch) { + constexpr std::size_t dimension(3UL); + //const std::size_t n_elems = vector_batch.columns(); + assert(vector_batch.rows() == dimension); + + return blaze::sqrt(blaze::trans(blaze::sum(vector_batch % vector_batch))); + } + +} // namespace rod diff --git a/backend/src/Utilities/Math/BlazeDetail/BlazeRotation.hpp b/backend/src/Utilities/Math/BlazeDetail/BlazeRotation.hpp new file mode 100644 index 000000000..26eef583a --- /dev/null +++ b/backend/src/Utilities/Math/BlazeDetail/BlazeRotation.hpp @@ -0,0 +1,152 @@ +#pragma once + +#include "blaze/Blaze.h" +#include "blaze_tensor/Blaze.h" + +namespace elastica { + // LOG AND EXP DEFINED AS PER ELASTICA'S ROTATIONAL DIRECTIONS! + + //************************************************************************** + /*!\brief Batchwise matrix logarithmic operator. + // + // \details + // Batchwise for rotation matrix R computes the corresponding rotation + // axis vector {theta (u)} using the matrix log() operator: \n + // if theta == 0: + // theta (u) = 0 \n + // else: + // theta (u) = -theta * inv_skew[R - transpose(R)] / sin(theta) \n + // where theta = acos(0.5 * (trace(R) - 1)) and inv_skew[] corresponds + // to an inverse skew symmetric mapping from a skew symmetric matrix M + // to vector V as: + //
+       |0 -z y|        |x|
+   M = |z 0 -x| to V = |y|
+       |-y x 0|        |z|
+   
+ // + // \example + // The following shows a typical use of the log_batch function + // with the expected (correct) result also shown. + // \snippet test_rotations.cpp log_batch_example + // + // \param[out] void/None + // \param[in] rot_matrix_batch(3, 3, n_elems) rotation matrix batch + // + // \return rot_axis_vector_batch(3, n_elems) rotation axis vector batch + // + // \see fill later? + */ + template // blaze Tensor expression type + void batch_inv_rotate(MT& rot_axis_vector_batch, const TT& rot_matrix_batch) { + constexpr std::size_t dimension(3UL); + const std::size_t n_elems = rot_matrix_batch.columns(); + using ValueType = typename TT::ElementType; + assert(rot_matrix_batch.pages() == dimension); + assert(rot_matrix_batch.rows() == dimension); + assert(rot_axis_vector_batch.rows() == dimension); + assert(rot_axis_vector_batch.columns() == n_elems); + + /* hardcoded to avoid external memory passing */ + /* also now operates only on views */ + auto rot_matrix_trace_batch = + blaze::row(blaze::pageslice(rot_matrix_batch, 0UL), 0UL) + + blaze::row(blaze::pageslice(rot_matrix_batch, 1UL), 1UL) + + blaze::row(blaze::pageslice(rot_matrix_batch, 2UL), 2UL); + /* theta = acos((tr(R) - 1) / 2) */ + auto theta_batch = blaze::acos(ValueType(0.5) * + (rot_matrix_trace_batch - ValueType(1.0) - + ValueType(1e-12))); // TODO refactor 1e-12 + auto R_minus_RT = + rot_matrix_batch - blaze::trans(rot_matrix_batch, {1, 0, 2}); // TODO Something is wrong + /* theta (u) = -theta * inv_skew([R - RT]) / 2 sin(theta) */ + for (std::size_t i(0UL); i < dimension; ++i) { + auto inv_skew_symmetric_map = blaze::row( + blaze::pageslice(R_minus_RT, (i + 2UL) % dimension), + (i + 1UL) % dimension); // % for the cyclic rotation of indices + blaze::row(rot_axis_vector_batch, i) = + ValueType(-0.5) * theta_batch * inv_skew_symmetric_map / + (blaze::sin(theta_batch) + + ValueType(1e-14)); // TODO refactor 1e-14 + } + } + //************************************************************************** + + //************************************************************************** + /*!\brief Batchwise matrix exponential operator. + // + // \details + // Batchwise for rotation axis vector {theta u} computes the corresponding + // rotation matrix R using the matrix exp() operator (Rodrigues formula): \n + // R = I - sin(theta) * U + (1 - cos(theta)) * U * U \n + // Here a different tensorial form is implemented as follows: + // if i == j: + // R{ij} = cos(theta) + (1 - cos(theta)) * (u{i})^2 \n + // else: + // R{ij} = (1 - cos(theta)) * u{i} * u{j} - sin(theta) * + // skew_sym[u]{ij} \n + // where I is the identity matrix and skew_sym[] corresponds to a skew + // symmetric mapping from a vector V to a skew symmetric matrix M as: + //
+       |x|        |0 -z y|
+   V = |y| to M = |z 0 -x|
+       |z|        |-y x 0|
+  
+ // + // \example + // The following shows a typical use of the exp_batch function + // with the expected (correct) result also shown. + // \snippet test_rotations.cpp exp_batch_example + // + // \param[out] rot_matrix_batch(3, 3, n_elems) rotation matrix batch + // \param[in] rot_axis_vector_batch(3, n_elems) rotation axis vector batch + // + // \return void/None + // + // \see fill later? + */ + template // blaze Matrix expression type + void exp_batch(TT& rot_matrix_batch, const MT& rot_axis_vector_batch) { + constexpr std::size_t dimension(3UL); + //const std::size_t n_elems = rot_axis_vector_batch.columns(); + using ValueType = typename MT::ElementType; + assert(rot_axis_vector_batch.rows() == dimension); + assert(rot_matrix_batch.pages() == dimension); + assert(rot_matrix_batch.rows() == dimension); + //assert(rot_matrix_batch.columns() == n_elems); + + auto theta_batch = blaze::sqrt( + blaze::sum(blaze::pow(rot_axis_vector_batch, 2))); + /* TODO refactor 1e-14 */ + auto unit_rot_axis_vector_batch = + rot_axis_vector_batch % + blaze::expand(blaze::pow(theta_batch + ValueType(1e-14), -1), + dimension); + for (std::size_t i(0UL); i < dimension; ++i) { + // if i == j: + // R{ij} = cos(theta) + (1 - cos(theta)) * (u{i})^2 + blaze::row(blaze::pageslice(rot_matrix_batch, i), i) = + blaze::cos(theta_batch) + + (ValueType(1.0) - blaze::cos(theta_batch)) * + blaze::pow(blaze::row(unit_rot_axis_vector_batch, i), 2); + // else: + // R{ij} = (1 - cos(theta)) * u{i} * u{j} - sin(theta) * + // skew_sym[u]{ij} + for (std::size_t j : {i + 1UL, i + 2UL}) { + auto skew_symmetric_map = + std::pow(-1, + j - i) * // Sign-bit to check order of entries + blaze::row(unit_rot_axis_vector_batch, + dimension - i - (j % dimension)); // % source index; + blaze::row(blaze::pageslice(rot_matrix_batch, i), j % dimension) = + (ValueType(1.0) - blaze::cos(theta_batch)) * + blaze::row(unit_rot_axis_vector_batch, i) * + blaze::row(unit_rot_axis_vector_batch, j % dimension) - + blaze::sin(theta_batch) * skew_symmetric_map; + } + } + } + //************************************************************************** + +} // namespace elastica diff --git a/backend/src/Utilities/Math/CMakeLists.txt b/backend/src/Utilities/Math/CMakeLists.txt new file mode 100644 index 000000000..d3a595afa --- /dev/null +++ b/backend/src/Utilities/Math/CMakeLists.txt @@ -0,0 +1,23 @@ +set(LIBRARY Utilities) + +elastica_target_sources( + ${LIBRARY} + PRIVATE +) + +elastica_target_headers( + ${LIBRARY} + INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/elastica + HEADERS + Rot3.hpp + Vec3.hpp + Types.hpp +) + +add_subdirectory(Python) + +target_link_libraries( + ${LIBRARY} + PRIVATE + Blaze +) diff --git a/backend/src/Utilities/Math/Rot3.hpp b/backend/src/Utilities/Math/Rot3.hpp new file mode 100644 index 000000000..c1e840b5d --- /dev/null +++ b/backend/src/Utilities/Math/Rot3.hpp @@ -0,0 +1,21 @@ +//============================================================================== +/*! +// \file +// \brief Header for a 3D matrix for use in \elastica interface +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +// +#include "Utilities/Math/Types.hpp" diff --git a/backend/src/Utilities/Math/Types.hpp b/backend/src/Utilities/Math/Types.hpp new file mode 100644 index 000000000..6c84669e7 --- /dev/null +++ b/backend/src/Utilities/Math/Types.hpp @@ -0,0 +1,50 @@ +//============================================================================== +/*! +// \file +// \brief Types belonging to the Math module +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +// +#include "Utilities/DefineTypes.h" + +namespace elastica { + + //**************************************************************************** + //! \brief 3D vector for use in \elastica interfaces + //! \ingroup math + using Vec3 = blaze::StaticVector; + //**************************************************************************** + + //**************************************************************************** + //! \brief 3D matrix for use in \elastica interfaces + //! \ingroup math + using Rot3 = blaze::StaticMatrix; + //**************************************************************************** + + ////////////////////////////////////////////////////////////////////////////// + // + // Forward declarations + // + ////////////////////////////////////////////////////////////////////////////// + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + struct RotationConvention; + /*! \endcond */ + //**************************************************************************** +} // namespace elastica diff --git a/backend/src/Utilities/Math/Vec3.hpp b/backend/src/Utilities/Math/Vec3.hpp new file mode 100644 index 000000000..f73a7d5be --- /dev/null +++ b/backend/src/Utilities/Math/Vec3.hpp @@ -0,0 +1,21 @@ +//============================================================================== +/*! +// \file +// \brief Header for a 3D vector for use in \elastica interface +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +// +#include "Utilities/Math/Types.hpp" From b3221cd2c49e24014db8ec59d051c0a63a2726c3 Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Fri, 14 Jun 2024 18:46:04 +0900 Subject: [PATCH 009/121] add blaze traits --- backend/src/Systems/CMakeLists.txt | 62 ++++ .../src/Systems/CosseratRods/CMakeLists.txt | 65 ++++ .../CosseratRods/Traits/CMakeLists.txt | 41 +++ .../CosseratRods/Traits/DataOpsTraits.hpp | 87 +++++ .../DataType/BlazeBackend/DataTypeTraits.hpp | 21 ++ .../DataType/BlazeBackend/MatrixTag.hpp | 281 ++++++++++++++ .../DataType/BlazeBackend/TensorTag.hpp | 344 ++++++++++++++++++ .../DataType/BlazeBackend/VectorTag.hpp | 274 ++++++++++++++ .../Traits/DataType/CMakeLists.txt | 40 ++ .../Traits/DataType/Protocols.hpp | 197 ++++++++++ .../CosseratRods/Traits/DataType/Rank.hpp | 40 ++ .../Traits/DataType/ScalarTag.hpp | 235 ++++++++++++ .../src/Systems/CosseratRods/Traits/Types.hpp | 35 ++ 13 files changed, 1722 insertions(+) create mode 100644 backend/src/Systems/CMakeLists.txt create mode 100644 backend/src/Systems/CosseratRods/CMakeLists.txt create mode 100644 backend/src/Systems/CosseratRods/Traits/CMakeLists.txt create mode 100644 backend/src/Systems/CosseratRods/Traits/DataOpsTraits.hpp create mode 100644 backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/DataTypeTraits.hpp create mode 100644 backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/MatrixTag.hpp create mode 100644 backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/TensorTag.hpp create mode 100644 backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/VectorTag.hpp create mode 100644 backend/src/Systems/CosseratRods/Traits/DataType/CMakeLists.txt create mode 100644 backend/src/Systems/CosseratRods/Traits/DataType/Protocols.hpp create mode 100644 backend/src/Systems/CosseratRods/Traits/DataType/Rank.hpp create mode 100644 backend/src/Systems/CosseratRods/Traits/DataType/ScalarTag.hpp create mode 100644 backend/src/Systems/CosseratRods/Traits/Types.hpp diff --git a/backend/src/Systems/CMakeLists.txt b/backend/src/Systems/CMakeLists.txt new file mode 100644 index 000000000..59bcc17ed --- /dev/null +++ b/backend/src/Systems/CMakeLists.txt @@ -0,0 +1,62 @@ +# Distributed under the MIT License. See LICENSE.txt for details. + +set(LIBRARY Systems) + +add_elastica_library(${LIBRARY} INTERFACE) + +# add_subdirectory(Block) +# add_library(${LIBRARY}::Block ALIAS Block) + +# add_subdirectory(common) +# add_library(${LIBRARY}::Common ALIAS SystemsCommon) + +add_subdirectory(CosseratRods) +add_library(${LIBRARY}::CosseratRods ALIAS CosseratRods) + +# add_subdirectory(Customization) +# add_library(${LIBRARY}::Customization ALIAS SystemsCustomization) + +# add_subdirectory(Python) + +# add_subdirectory(RigidBody) +# add_library(${LIBRARY}::RigidBody ALIAS RigidBody) + +# add_subdirectory(States) +# add_library(${LIBRARY}::States ALIAS States) + +elastica_target_headers( + ${LIBRARY} + INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/elastica + # Block.hpp + CosseratRods.hpp + # Protocols.hpp + # RigidBody.hpp + # Sphere.hpp + # Systems.hpp + # Tags.hpp + # Types.hpp +) + +elastica_target_sources( + ${LIBRARY} + INTERFACE + # Block.hpp + CosseratRods.hpp + # Protocols.hpp + # RigidBody.hpp + # Sphere.hpp + # Systems.hpp + # Tags.hpp + # Types.hpp +) + +target_link_libraries( + ${LIBRARY} + INTERFACE + # Block + # SystemsCommon + CosseratRods + # SystemsCustomization + # RigidBody + # States +) diff --git a/backend/src/Systems/CosseratRods/CMakeLists.txt b/backend/src/Systems/CosseratRods/CMakeLists.txt new file mode 100644 index 000000000..78cd4c5b1 --- /dev/null +++ b/backend/src/Systems/CosseratRods/CMakeLists.txt @@ -0,0 +1,65 @@ +# Distributed under the MIT License. See LICENSE.txt for details. + +set(LIBRARY CosseratRods) +add_elastica_library(${LIBRARY} INTERFACE) + +# set(LIBRARY_SOURCES +# Access.hpp +# Aliases.hpp +# Block.hpp +# BlockInitializer.hpp +# BlockSlice.hpp +# BlockView.hpp +# Components.hpp +# CosseratRodPlugin.hpp +# CosseratRods.hpp +# CosseratRodTraits.hpp +# Initializers.hpp +# Protocols.hpp +# Serialize.hpp # TODO : refactor +# Specialize.hpp +# Tags.hpp +# Types.hpp +# TypeTraits.hpp +# Utility.hpp +# ) + +elastica_target_headers( + ${LIBRARY} + INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/elastica + ${LIBRARY_SOURCES} +) + +elastica_target_sources( + ${LIBRARY} + INTERFACE + ${LIBRARY_SOURCES} +) + +#add_subdirectory(Aliases) +#add_library(${LIBRARY}::Aliases ALIAS CosseratRodAliases) + +#add_subdirectory(Components) +#add_library(${LIBRARY}::Components ALIAS CosseratRodComponents) + +#add_subdirectory(Initializers) +#add_library(${LIBRARY}::Initializers ALIAS CosseratRodInitializers) + +add_subdirectory(Traits) +add_library(${LIBRARY}::Traits ALIAS CosseratRodTraits) + +#add_subdirectory(Utility) +#add_library(${LIBRARY}::Utility ALIAS CosseratRodUtility) + +target_link_libraries( + ${LIBRARY} + INTERFACE + CosseratRodAliases + CosseratRodComponents + CosseratRodInitializers + CosseratRodTraits + CosseratRodUtility + Utilities +) + +add_subdirectory(Python) diff --git a/backend/src/Systems/CosseratRods/Traits/CMakeLists.txt b/backend/src/Systems/CosseratRods/Traits/CMakeLists.txt new file mode 100644 index 000000000..8aad7e44c --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/CMakeLists.txt @@ -0,0 +1,41 @@ +# Distributed under the MIT License. See LICENSE.txt for details. + +set(LIBRARY CosseratRodTraits) + +add_elastica_library(${LIBRARY} INTERFACE) + +elastica_target_headers( + ${LIBRARY} + INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/elastica + DataOpsTraits.hpp + # PlacementTrait.hpp + Protocols.hpp + Types.hpp +) + +elastica_target_sources( + ${LIBRARY} + INTERFACE + DataOpsTraits.hpp + # PlacementTrait.hpp + Protocols.hpp + Types.hpp +) + +add_subdirectory(DataType) +add_library(${LIBRARY}::DataTraits ALIAS CosseratRodDataTraits) + +# add_subdirectory(PlacementTraits) +# add_library(${LIBRARY}::PlacementTraits ALIAS CosseratRodPlacementTraits) + +add_subdirectory(Operations) +add_library(${LIBRARY}::OperationTraits ALIAS CosseratRodOperationTraits) + +target_link_libraries( + ${LIBRARY} + INTERFACE + CosseratRodDataTraits + # CosseratRodPlacementTraits + CosseratRodOperationTraits +) + diff --git a/backend/src/Systems/CosseratRods/Traits/DataOpsTraits.hpp b/backend/src/Systems/CosseratRods/Traits/DataOpsTraits.hpp new file mode 100644 index 000000000..cb0d0e816 --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/DataOpsTraits.hpp @@ -0,0 +1,87 @@ +//============================================================================== +/*! +// \file +// \brief Datatraits for Cosserat Rods within \elastica +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +/// +#include "Systems/CosseratRods/Traits/Types.hpp" +#include "Utilities/DefineTypes.h" +/// +//#include "Systems/CosseratRods/Traits/DataTraits/Rank.hpp" +// Not used, since we directly use blaze's implementation of a vector +//#include "Systems/CosseratRods/Traits/DataType/ScalarTag.hpp" +/// + +#define ELASTICA_USE_BLAZE 1 // todo : from CMAKE + +#if defined(ELASTICA_USE_BLAZE) +#include "DataType/BlazeBackend/DataTypeTraits.hpp" +#include "Operations/BlazeBackend/OpsTraits.hpp" +#include "Operations/BlazeBackend/OptimizationLevel.hpp" +#endif // ELASTICA_USE_BLAZE + +/// +#include "Utilities/NonCreatable.hpp" + +namespace elastica { + + namespace cosserat_rod { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*!\brief Traits implementing data-structures for Cosserat rods in \elastica + * \ingroup cosserat_rod_traits + * + * \details + * DataOpsTraits is the customization point for altering the data-structures + * to be used within the Cosserat rod hierarchy implemented using @ref + * blocks in \elastica. It defines (domain-specific) types corresponding to + * a Cosserat rod (such as a matrix, vector etc.), and is intended for use + * as a template parameter in CosseratRodTraits. + * + * \see elastica::cosserat_rod::CosseratRodTraits + */ + struct DataOpsTraits : private NonCreatable { + //**Type definitions****************************************************** + //! Real type + using real_type = real_t; + //! Type of index + using index_type = std::size_t; + // using OnRod = ScalarTag; + //! Type to track indices on a Cosserat rod, with shape (1, ) + using Index = VectorTag; + //! Type to track scalars on a Cosserat rod, with shape (1, ) + using Scalar = VectorTag; + //! Type to track vectors on a Cosserat rod, with shape (d) + using Vector = MatrixTag; + //! Type to track matrices on a Cosserat rod, with shape (d, d) + using Matrix = TensorTag; + //************************************************************************ + + //**Operation definitions************************************************* + using Operations = OpsTraits; + //************************************************************************ + }; + //************************************************************************** + + } // namespace cosserat_rod +} // namespace elastica \ No newline at end of file diff --git a/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/DataTypeTraits.hpp b/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/DataTypeTraits.hpp new file mode 100644 index 000000000..2681724a6 --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/DataTypeTraits.hpp @@ -0,0 +1,21 @@ +//============================================================================== +/*! +// \file +// \brief Blaze data-traits for Cosserat Rods within \elastica +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/CosseratRods/Traits/DataType/BlazeBackend/MatrixTag.hpp" +#include "Systems/CosseratRods/Traits/DataType/BlazeBackend/TensorTag.hpp" +#include "Systems/CosseratRods/Traits/DataType/BlazeBackend/VectorTag.hpp" diff --git a/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/MatrixTag.hpp b/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/MatrixTag.hpp new file mode 100644 index 000000000..f91f2a146 --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/MatrixTag.hpp @@ -0,0 +1,281 @@ +//============================================================================== +/*! +// \file +// \brief Tag indicating a matrix (Rank 1) for Cosserat Rods within \elastica +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Configuration/Systems.hpp" // For index checking +// assert +#include "ErrorHandling/Assert.hpp" +#include "Systems/CosseratRods/Traits/DataType/Protocols.hpp" +#include "Systems/CosseratRods/Traits/DataType/Rank.hpp" + +// matrix types +#include +// #include +#include +#include +#include + +// this comes from the simulator module, which seems like an anti-pattern +// should be in Systems instead +#include "Simulator/Frames.hpp" +#include "Utilities/ProtocolHelpers.hpp" + +namespace elastica { + + namespace cosserat_rod { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*!\brief Tag for a Rank 2 tensor within Cosserat rods in \elastica + * \ingroup cosserat_rod_traits + * + * \details + * MatrixTag is used within the Cosserat rod components of \elastica to + * indicate a vector. + * + * \tparam Real a floating point type + */ + template + struct MatrixTag : public ::tt::ConformsTo { + //**Type definitions****************************************************** + //! The main type for a TaggedTuple + using type = + blaze::DynamicMatrix /*, TagType*/>; + //! Data type for better readability + using data_type = type; + //! The type of slice for the data type + using slice_type = blaze::Submatrix_; + //! The type of const slice for the data type + using const_slice_type = + const blaze::Submatrix_; + //! The type of a ghost element for the data type + using ghost_type = blaze::StaticVector; + //! The type of a reference to the underlying data type + using reference_type = blaze::Column_; + //! The type of const reference to the underlying data type + using const_reference_type = const blaze::Column_; + //! The type of reference to the slice type + using reference_slice_type = + blaze::Subvector_, blaze::unaligned>; + //! The type of const reference to the slice type + using const_reference_slice_type = + const blaze::Subvector_, + blaze::unaligned>; + //! The rank of the data type + using rank = Rank<2U>; + //************************************************************************ + + //**Utility functions***************************************************** + /*!\name Utility functions */ + //@{ + + //************************************************************************ + /*!\brief Obtain the ghost value + * + * \details + * Obtains a default value for putting in ghost elements. This function + * can be overriden in case the user wants to. This should not be a + * Real (because implicit conversion fills entire data-structure with + * that particular value) but rather a ghost_type object (like a matrix + * for example) + */ + static inline constexpr auto ghost_value() noexcept -> ghost_type { + constexpr Real zero(0.0); + return ghost_type{zero, zero, zero}; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the data type at a given index + * + * \details + * Overload to obtain a slice of data at `index`. + * It is typically used in the block to generate BlockSlices or access + * indexed data + * + * \param data Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(data_type& data, std::size_t index) { + return blaze::column(data, index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the const data type at a given index + * + * \details + * Overload to obtain a slice of const data at `index`. + * It is typically used in the block to generate ConstBlockSlices or + * access indexed data + * + * \param data Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(data_type const& data, + std::size_t index) { + return blaze::column(data, index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain subslice of the slice type at a given index + * + * \details + * Overload to obtain a subslice of slice at `index`. + * It is typically used in the block to generate slices that are then + * filled in by ghost values. + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(slice_type& slice, std::size_t index) { + return blaze::column(slice, index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain sub-slice of the const slice type at a given index + * + * \details + * Overload to obtain a subslice of const slice at `index`. + * + * It is sometimes needed in a Plugin where the context is const, but the + * data of the underlying structure may well be modified. + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(slice_type const& slice, + std::size_t index) { + return blaze::column(slice, index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain subslice of a temporary slice type at a given index + * + * \details + * Overload to obtain a subslice of slice at `index`. + * + * It is typically used when lazily generating individual slices from a + * lazily generated slice : for example blockslice.get_position(i), where + * get_position() lazily generates a slice, which then gets further + * sliced. + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(slice_type&& slice, + std::size_t index) { + return blaze::column(static_cast(slice), index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain sub-slice of the const_slice type at a given index + * + * \details + * Overload to obtain a subslice of const_slice at `index`. + * It is needed in a Plugin where the slice itself is const, such as a + * ConstBlockSlice + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(const_slice_type const& slice, + std::size_t index) { + return blaze::column(slice, index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Resize the data type to contain at least `size` entries + * + * \param data The data to be resized + * \param new_size New size of data + */ + static inline void resize(data_type& data, std::size_t new_size) { + ELASTICA_ASSERT(data.columns() <= new_size, + "Contract violation, block shrinks"); + return data.resize(Frames::Dimension, new_size, true); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the data type through a set of indices + * + * \details + * Overload to obtain a slice of `size` amount from data at `start`. + * This is typically used in the block to generate slices that are to be + * filled in by the class hierarchies, when filling in a block + * incrementally. + * + * \param data Data to be sliced + * \param start_index Start index + * \param size Size of the data + */ + static inline decltype(auto) slice(data_type& data, + std::size_t start_index, + std::size_t size) { + return blaze::submatrix(data, 0UL, start_index, Frames::Dimension, size, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the const data type through a set of indices + * + * \details + * Overload to obtain a slice of `size` amount from const data at `start`. + * This is typically used in the block to generate slices that are to be + * filled in by the class hierarchies, when filling in a block + * incrementally. + * + * \param data Data to be sliced + * \param start_index Start index + * \param size Size of the data + */ + static inline decltype(auto) slice(data_type const& data, + std::size_t start_index, + std::size_t size) { + return blaze::submatrix(data, 0UL, start_index, Frames::Dimension, size, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //@} + //************************************************************************ + }; + //************************************************************************** + + } // namespace cosserat_rod + +} // namespace elastica diff --git a/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/TensorTag.hpp b/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/TensorTag.hpp new file mode 100644 index 000000000..0dd00491d --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/TensorTag.hpp @@ -0,0 +1,344 @@ +//============================================================================== +/*! +// \file +// \brief Tag indicating a Rank 3 tensor for Cosserat Rods within \elastica +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Configuration/Systems.hpp" // For index checking + +// assert +#include "ErrorHandling/Assert.hpp" + +// Rank +#include "Systems/CosseratRods/Traits/DataType/Protocols.hpp" +#include "Systems/CosseratRods/Traits/DataType/Rank.hpp" + +// Tensor types +#include +#include +#include +#include +#include +#include + +// this comes from the simulator module, which seems like an anti-pattern +// should be in Systems instead +#include "Simulator/Frames.hpp" +#include "Utilities/Math/Vec3.hpp" +#include "Utilities/ProtocolHelpers.hpp" + +namespace elastica { + + namespace cosserat_rod { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*!\brief Tag for a Rank 3 tensor within Cosserat rods in \elastica + * \ingroup cosserat_rod_traits + * + * \details + * TensorTag is used within the Cosserat rod components of \elastica to + * indicate a tensor. + * + * \tparam Real a floating point type + */ + template + struct TensorTag : public ::tt::ConformsTo { + //**Type definitions****************************************************** + //! The main type for a TaggedTuple + using type = blaze::DynamicTensor; + //! Data type for better readability + using data_type = type; + //! The type of slice for the data type + using slice_type = blaze::Subtensor_; + //! The type of const slice for the data type + using const_slice_type = + const blaze::Subtensor_; + /* + * Developer note: + * Blaze tensor cant initialize with a matrix type + * so the ghosts for now are tensor types which involve + * more work than necessary + * + * using ghost_type = blaze::DynamicMatrix; + * blaze::StaticMatrix; + */ + //! The type of a ghost element for the data type + using ghost_type = + blaze::StaticMatrix; + //! The type of a reference to the underlying data type + using reference_type = blaze::ColumnSlice_; + //! The type of const reference to the underlying data type + using const_reference_type = const blaze::ColumnSlice_; + //! The type of reference to the slice type + using reference_slice_type = + blaze::Submatrix_, blaze::unaligned>; + //! The type of const reference to the slice type + using const_reference_slice_type = + const blaze::Submatrix_, + blaze::unaligned>; + //! The rank of the data type + using rank = Rank<3U>; + //************************************************************************ + + //**Utility functions***************************************************** + /*!\name Utility functions */ + //@{ + + //************************************************************************ + /*!\brief Obtain the ghost value + * + * \details + * Obtains a default value for putting in ghost elements. This function + * can be overriden in case the user wants to. This should not be a + * Real (because implicit conversion fills entire data-structure with + * that particular value) but rather a ghost_type object (like a matrix + * for example) + */ + static inline auto ghost_value() noexcept -> ghost_type { + // cannot be constexpr as it involves allocation :/ + constexpr Real zero(0.0); + constexpr Real identity(1.0); + return ghost_type{{{identity}, {zero}, {zero}}, + {{zero}, {identity}, {zero}}, + {{zero}, {zero}, {identity}}}; + } + //************************************************************************ + + // should really be in the Slice Tag class, but it ends up being + // confusing as what we are really doing is taking a slice of a + // slice but its abstracted away + // template + // static inline constexpr decltype(auto) slice(MT& matrix, + // std::size_t i) + + // TODO decltype expression is same as SliceType + // TODO This is internal function, need not be exposed + // + // + + //************************************************************************ + /*!\brief Obtain slice of the data type at a given index + * + * \details + * Overload to obtain a slice of data at `index`. + * It is typically used in the block to generate BlockSlices or access + * indexed data + * + * \param data Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(data_type& data, std::size_t index) { + return blaze::columnslice(data, index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the const data type at a given index + * + * \details + * Overload to obtain a slice of const data at `index`. + * It is typically used in the block to generate ConstBlockSlices or + * access indexed data + * + * \param data Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(data_type const& data, + std::size_t index) { + return blaze::columnslice(data, index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain a sub-slice of the slice at a given index + * + * \details + * Overload to obtain a subslice of slice at `index`. + * It is typically used in the block to generate slices that are then + * filled in by ghost values. + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(slice_type& slice, std::size_t index) { + return blaze::columnslice(slice, index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + // return blaze::subtensor(slice, 0UL, 0UL, index, Frames::Dimension, + // Frames::Dimension, 1UL); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain a sub-const slice of the const slice at a given index + * + * \details + * Overload to obtain a subslice of const slice at `index`. + * + * It is sometimes needed in a Plugin where the context is const, but the + * data of the underlying structure may well be modified. + * + * \param slice Slice to be further const sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(slice_type const& slice, + std::size_t index) { + return blaze::columnslice(slice, index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain a sub-slice of the slice at a given index + * + * \details + * Overload to obtain a subslice of slice at `index`. + * + * It is typically used when lazily generating individual slices from a + * lazily generated slice : for example blockslice.get_position(i), where + * get_position() lazily generates a slice, which then gets further + * sliced. + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(slice_type&& slice, + std::size_t index) { + return blaze::columnslice(static_cast(slice), index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain sub-slice of the const_slice type at a given index + * + * \details + * Overload to obtain a subslice of const_slice at `index`. + * It is needed in a Plugin where the slice itself is const, such as a + * ConstBlockSlice + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(const_slice_type const& slice, + std::size_t index) { + return blaze::columnslice(slice, index, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Resize the data type to contain at least `size` entries + * + * \param data The data to be resized + * \param new_size New size of data + */ + static inline void resize(data_type& data, std::size_t new_size) { + ELASTICA_ASSERT(data.columns() <= new_size, + "Contract violation, block shrinks"); + // (o, m, n) + return data.resize(Frames::Dimension, Frames::Dimension, new_size, + true); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the data type through a set of indices + * + * \details + * Overload to obtain a slice of `size` amount from data at `start`. + * This is typically used in the block to generate slices that are to be + * filled in by the class hierarchies, when filling in a block + * incrementally. With the default size, it is typically used in the + * block to generate slices that are to be filled in by ghost values. + * + * \param data Data to be sliced + * \param start_index Start index + * \param size Size of the data + */ + static inline decltype(auto) slice(data_type& data, + std::size_t start_index, + std::size_t size) { + // (page, row, column), (o, m, n) + return blaze::subtensor(data, 0UL, 0UL, start_index, Frames::Dimension, + Frames::Dimension, size, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the const data type through a set of indices + * + * \details + * Overload to obtain a slice of `size` amount from const data at `start`. + * This is typically used in the block to generate slices that are to be + * filled in by the class hierarchies, when filling in a block + * incrementally. With the default size, it is typically used in the + * block to generate slices that are to be filled in by ghost values. + * + * \param data Data to be sliced + * \param start_index Start index + * \param size Size of the data + */ + static inline decltype(auto) slice(data_type const& data, + std::size_t start_index, + std::size_t size) { + // (page, row, column), (o, m, n) + return blaze::subtensor(data, 0UL, 0UL, start_index, Frames::Dimension, + Frames::Dimension, size, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Assign diagonal elements + * + * @return + */ + //************************************************************************ + static inline void diagonal_assign(slice_type& data, std::size_t index, + Vec3 const& diag) { + auto submatrix = blaze::columnslice( + data, index, blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + submatrix(0UL, 0UL) = static_cast(diag[0UL]); + submatrix(1UL, 1UL) = static_cast(diag[1UL]); + submatrix(2UL, 2UL) = static_cast(diag[2UL]); + } + + static inline void diagonal_assign(data_type& data, std::size_t index, + Vec3 const& diag) { + auto submatrix = blaze::columnslice( + data, index, blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + submatrix(0UL, 0UL) = static_cast(diag[0UL]); + submatrix(1UL, 1UL) = static_cast(diag[1UL]); + submatrix(2UL, 2UL) = static_cast(diag[2UL]); + } + + //@} + //************************************************************************ + }; + //************************************************************************** + + } // namespace cosserat_rod + +} // namespace elastica diff --git a/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/VectorTag.hpp b/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/VectorTag.hpp new file mode 100644 index 000000000..38e77d333 --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/DataType/BlazeBackend/VectorTag.hpp @@ -0,0 +1,274 @@ +//============================================================================== +/*! +// \file +// \brief Tag indicating a vector (Rank 1) for Cosserat Rods within \elastica +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Configuration/Systems.hpp" // For index checking +// assert +#include "ErrorHandling/Assert.hpp" +#include "Systems/CosseratRods/Traits/DataType/Protocols.hpp" +#include "Systems/CosseratRods/Traits/DataType/Rank.hpp" + +// Vector types +#include +#include +#include +#include + +#include "Utilities/ProtocolHelpers.hpp" + +namespace elastica { + + namespace cosserat_rod { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*!\brief Tag for a Rank 1 tensor within Cosserat rods in \elastica + * \ingroup cosserat_rod_traits + * + * \details + * VectorTag is used within the Cosserat rod components of \elastica to + * indicate a vector. + * + * \tparam Real a floating point type + */ + template + struct VectorTag : public ::tt::ConformsTo { + //**Type definitions****************************************************** + //! The main type for a TaggedTuple + /* + */ + using type = + blaze::DynamicVector /*, TagType*/>; + //! Data type for better readability + using data_type = type; + //! The type of slice for the data type + using slice_type = blaze::Subvector_; + //! The type of a const slice for the data type + using const_slice_type = + const blaze::Subvector_; + //! The type of a ghost element for the data type + using ghost_type = Real; + //! The type of a reference to the underlying data type + using reference_type = Real&; + //! The type of const reference to the underlying data type + using const_reference_type = Real const&; + //! The type of reference to the slice type + using reference_slice_type = Real&; + //! The type of const reference to the slice type + using const_reference_slice_type = Real const&; + //! The rank of the data type + using rank = Rank<1U>; + //************************************************************************ + + //**Utility functions***************************************************** + /*!\name Utility functions */ + //@{ + + //************************************************************************ + /*!\brief Obtain the ghost value + * + * \details + * Obtains a default value for putting in ghost elements. This function + * can be overriden in case the user wants to. This should not be a + * Real (because implicit conversion fills entire data-structure with + * that particular value) but rather a ghost_type object (like a matrix + * for example) + */ + static inline constexpr auto ghost_value() noexcept -> ghost_type { + return ghost_type(0.0); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the data type at a given index + * + * \details + * Overload to obtain a slice of data at `index`. + * It is typically used in the block to generate BlockSlices or access + * indexed data + * + * \param data Slice to be further sliced + * \param index Index of slicing + */ + static inline auto slice(data_type& data, std::size_t index) + -> reference_type { + return data[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the const data type at a given index + * + * \details + * Overload to obtain a slice of const data at `index`. + * It is typically used in the block to generate ConstBlockSlices or + * access indexed data + * + * \param data Slice to be further sliced + * \param index Index of slicing + */ + static inline auto slice(data_type const& data, std::size_t index) + -> const_reference_type { + return data[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain a sub-slice of the slice at a given index + * + * \details + * Overload to obtain a subslice of slice at `index`. + * It is typically used in the block to generate slices that are then + * filled in by ghost values. + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline auto slice(slice_type& slice, std::size_t index) + -> reference_type { + return slice[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain a sub-slice of the const slice at a given index + * + * \details + * Overload to obtain a slice of data at `index`. + * It is sometimes needed in a Plugin where the context is const, but the + * data of the underlying structure may well be modified. + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline auto slice(slice_type const& slice, std::size_t index) + -> const_reference_type { + return slice[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain a sub-slice of the slice at a given index + * + * \details + * Overload to obtain a subslice of slice at `index`. + * + * It is typically used when lazily generating individual slices from a + * lazily generated slice : for example blockslice.get_position(i), where + * get_position() lazily generates a slice, which then gets further + * sliced. + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(slice_type&& slice, + std::size_t index) { + return static_cast(slice)[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain sub-slice of the const_slice type at a given index + * + * \details + * Overload to obtain a subslice of const_slice at `index`. + * It is needed in a Plugin where the slice itself is const, such as a + * ConstBlockSlice + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(const_slice_type const& slice, + std::size_t index) { + return slice[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Resize the data type to contain at least `size` entries + * + * \param data The data to be resized + * \param new_size New size of data + */ + static inline void resize(data_type& data, std::size_t new_size) { + ELASTICA_ASSERT(data.size() <= new_size, + "Contract violation, block shrinks"); + return data.resize(new_size, true); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the data type through a set of indices + * + * \details + * Overload to obtain a slice of `size` amount from data at `start`. + * This is typically used in the block to generate slices that are to be + * filled in by the class hierarchies, when filling in a block + * incrementally. + * + * \param data Data to be sliced + * \param start_index Start index + * \param size Size of the data + */ + static inline auto slice(data_type& data, std::size_t start_index, + std::size_t size) -> slice_type { + return blaze::subvector(data, start_index, size, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + static inline auto slice(slice_type& data, std::size_t start_index, + std::size_t size) -> slice_type { + return blaze::subvector(data, start_index, size, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the const data type through a set of indices + * + * \details + * Overload to obtain a slice of `size` amount from const data at `start`. + * This is typically used in the block to generate slices that are to be + * filled in by the class hierarchies, when filling in a block + * incrementally. + * + * \param data Data to be sliced + * \param start_index Start index + * \param size Size of the data + */ + static inline auto slice(data_type const& data, std::size_t start_index, + std::size_t size) -> const_slice_type { + // unaligned always + return blaze::subvector(data, start_index, size, + blaze::ELASTICA_DEFAULT_SYSTEM_INDEX_CHECK); + } + //************************************************************************ + + //@} + //************************************************************************ + }; + //************************************************************************** + + } // namespace cosserat_rod + +} // namespace elastica diff --git a/backend/src/Systems/CosseratRods/Traits/DataType/CMakeLists.txt b/backend/src/Systems/CosseratRods/Traits/DataType/CMakeLists.txt new file mode 100644 index 000000000..837a312a2 --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/DataType/CMakeLists.txt @@ -0,0 +1,40 @@ +# Distributed under the MIT License. See LICENSE.txt for details. + +set(LIBRARY CosseratRodDataTraits) + +add_elastica_library(${LIBRARY} INTERFACE) + +elastica_target_headers( + ${LIBRARY} + INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/elastica + BlazeBackend/DataTypeTraits.hpp + BlazeBackend/MatrixTag.hpp + BlazeBackend/TensorTag.hpp + BlazeBackend/VectorTag.hpp + Protocols.hpp + Rank.hpp + ScalarTag.hpp +) + +elastica_target_sources( + ${LIBRARY} + INTERFACE + BlazeBackend/DataTypeTraits.hpp + BlazeBackend/MatrixTag.hpp + BlazeBackend/TensorTag.hpp + BlazeBackend/VectorTag.hpp + Protocols.hpp + Rank.hpp + ScalarTag.hpp +) + + +target_link_libraries( + ${LIBRARY} + INTERFACE + ModuleSettings + Utilities + Blaze + BlazeTensor +) + diff --git a/backend/src/Systems/CosseratRods/Traits/DataType/Protocols.hpp b/backend/src/Systems/CosseratRods/Traits/DataType/Protocols.hpp new file mode 100644 index 000000000..500c89297 --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/DataType/Protocols.hpp @@ -0,0 +1,197 @@ +//============================================================================== +/*! +// \file +// \brief Data-trait protocols +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include // for size_t +#include // for declval + +#include "Utilities/TypeTraits.hpp" + +namespace elastica { + + namespace cosserat_rod { + + namespace protocols { + + //======================================================================== + // + // CLASS DEFINITION + // + //======================================================================== + + //************************************************************************ + /*!\brief Data-trait protocol. + * \ingroup cosserat_rod_traits + * + * Class to enforce adherence to a Cosserat Rod Data-trait protocol. Any + * valid data trait class within the \elastica library should (publicly) + * inherit from this class to indicate it qualifies as a data-trait. Only + * in case a class is derived publicly from this base class, the + * tt::conforms_to and tt::assert_conforms_to type traits recognizes the + * class as valid data traits. + * + * Requires that a conforming type `ConformingType` has these nested types + * \snippet this expected_types + * and these static member functions, + * \snippet this expected_static_functions + * + * \example + * The following shows an example of minimal conformance to this protocol. + * With the setup shown here + * \snippet Mocks/CosseratRodTraits.hpp vector_datatrait + * we ensure conformance as + * \snippet DataType/Test_Protocols.cpp datatrait_protocol_eg + */ + struct DataTrait { + //********************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Auxiliary helper struct for enforcing protocols. + // \ingroup protocols + */ + template + struct test { + public: + /// [expected_types] + //**Type definitions************************************************** + //! The main type for a TaggedTuple + using data_type = typename ConformingType::type; + //! The type of slice for the data type + using slice_type = typename ConformingType::slice_type; + //! The type of const slice for the data type + using const_slice_type = typename ConformingType::const_slice_type; + //! The type of a reference to the underlying data type + using reference_type = typename ConformingType::reference_type; + //! The type of const reference to the underlying data type + using const_reference_type = + typename ConformingType::const_reference_type; + //! The type of reference to the slice type + using reference_slice_type = + typename ConformingType::reference_slice_type; + //! The type of const reference to the slice type + using const_reference_slice_type = + typename ConformingType::const_reference_slice_type; + //! The type of a ghost element for the data type + using ghost_type = typename ConformingType::ghost_type; + // //! The rank of the the data type + // using rank = typename ConformingType::rank; + //******************************************************************** + /// [expected_types] + + /// [expected_static_functions] + // Function prototypes needing no arguments + using ghost_value_return_type = + decltype(ConformingType::ghost_value()); + static_assert(cpp17::is_same_v, + R"error( +Not a conforming data trait, doesn't properly implement static function +`ghost_type ghost_value()`)error"); + + // Function prototypes needing two arguments + using index = std::size_t; + using slice_at_index_return_type = decltype(ConformingType::slice( + std::declval(), std::declval())); + static_assert( + cpp17::is_same_v, + R"error( +Not a conforming data trait, doesn't properly implement static function +`auto slice(data_type& data, std::size_t index)` +)error"); + + using const_slice_at_index_return_type = + decltype(ConformingType::slice(std::declval(), + std::declval())); + static_assert(cpp17::is_same_v, + R"error( +Not a conforming data trait, doesn't properly implement static function +`auto slice(data_type const& data, std::size_t index)` +)error"); + + using slice_of_slice_at_index_return_type = + decltype(ConformingType::slice(std::declval(), + std::declval())); + static_assert(cpp17::is_same_v, + R"error( +Not a conforming data trait, doesn't properly implement static function +`auto slice(slice_type& slice, std::size_t index)` +)error"); + + using slice_of_const_slice_at_index_return_type = + decltype(ConformingType::slice(std::declval(), + std::declval())); + static_assert( + cpp17::is_same_v, + R"error( +Not a conforming data trait, doesn't properly implement static function +`auto slice(slice_type const& slice, std::size_t index)` +)error"); + + using const_slice_of_const_slice_at_index_return_type = + decltype(ConformingType::slice( + std::declval(), + std::declval())); + static_assert( + cpp17::is_same_v, + R"error( +Not a conforming data trait, doesn't properly implement static function +`auto slice(const_slice_type const& slice, std::size_t index)` +)error"); + + using new_dofs = std::size_t; + using resize_return_type = decltype(ConformingType::resize( + std::declval(), std::declval())); + static_assert(cpp17::is_same_v, + R"error( +Not a conforming data trait, doesn't properly implement static function +`void resize(data_type& data, std::size_t new_dofs)`)error"); + + // Function prototypes needing three arguments + using start = std::size_t; + using size = std::size_t; + using slice_return_type = decltype(ConformingType::slice( + std::declval(), std::declval(), + std::declval())); + static_assert(cpp17::is_same_v, + R"error( +Not a conforming data trait, doesn't properly implement static function +`slice_type slice(data_type& data, std::size_t start, std::size_t size)`)error"); + + using const_slice_return_type = decltype(ConformingType::slice( + std::declval(), std::declval(), + std::declval())); + static_assert( + cpp17::is_same_v, + R"error( +Not a conforming data trait, doesn't properly implement static function +`const_slice_type slice(data_type const& data, std::size_t start, std::size_t size)`)error"); + + /// [expected_static_functions] + }; + /*! \endcond */ + //********************************************************************** + }; + //************************************************************************ + + } // namespace protocols + + } // namespace cosserat_rod + +} // namespace elastica diff --git a/backend/src/Systems/CosseratRods/Traits/DataType/Rank.hpp b/backend/src/Systems/CosseratRods/Traits/DataType/Rank.hpp new file mode 100644 index 000000000..85ecc7eda --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/DataType/Rank.hpp @@ -0,0 +1,40 @@ +//============================================================================== +/*! +// \file +// \brief Marks a rank of a tensor +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +#include + +namespace elastica { + + namespace cosserat_rod { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Marks a rank of a tensor + // \ingroup cosserat_rod + */ + template + struct Rank : std::integral_constant {}; + /*! \endcond */ + //************************************************************************** + + } // namespace cosserat_rod + +} // namespace elastica diff --git a/backend/src/Systems/CosseratRods/Traits/DataType/ScalarTag.hpp b/backend/src/Systems/CosseratRods/Traits/DataType/ScalarTag.hpp new file mode 100644 index 000000000..6e22fa8ff --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/DataType/ScalarTag.hpp @@ -0,0 +1,235 @@ +//============================================================================== +/*! +// \file +// \brief Tag indicating a vector (Rank 0) for Cosserat Rods within \elastica +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +// Vector types +#include // ref wrapper +#include + +// assert +#include "ErrorHandling/Assert.hpp" +#include "Systems/CosseratRods/Traits/DataTraits/Protocols.hpp" +#include "Systems/CosseratRods/Traits/DataTraits/Rank.hpp" +#include "Utilities/NoSuchType.hpp" + +namespace elastica { + + namespace cosserat_rod { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*!\brief Tag for a Rank 1 tensor within Cosserat rods in \elastica + * \ingroup cosserat_rod_traits + * + * \details + * ScalarTag is used within the Cosserat rod components of \elastica to + * indicate a vector. + * + * \tparam RealOrIndex a floating point (or) index type + */ + template + struct ScalarTag : public ::tt::ConformsTo { + //**Type definitions****************************************************** + //! The main type for a TaggedTuple + using type = std::vector; + //! Data type for better readability + using data_type = type; + //! The type of slice for the data type + using slice_type = std::vector>; + //! The type of a const slice for the data type + using const_slice_type = + const std::vector>; + //! The type of a ghost element for the data type + using ghost_type = NoSuchType; + //! The type of a reference to the underlying data type + using reference_type = RealOrIndex&; + //! The type of const reference to the underlying data type + using const_reference_type = RealOrIndex const&; + //! The type of reference to the slice type + using reference_slice_type = RealOrIndex&; + //! The type of const reference to the slice type + using const_reference_slice_type = RealOrIndex const&; + //! The rank of the data type + using rank = Rank<0U>; + //************************************************************************ + + //**Utility functions***************************************************** + /*!\name Utility functions */ + //@{ + + //************************************************************************ + /*!\brief Obtain the ghost value + * + * \details + * Obtains a default value for putting in ghost elements. This function + * can be overriden in case the user wants to. This should not be a + * RealOrIndex (because implicit conversion fills entire data-structure + * with that particular value) but rather a ghost_type object (like a + * matrix for example) + */ + static inline constexpr auto ghost_value() noexcept -> ghost_type { + return {}; + } + //********************************************************************** + + //************************************************************************ + /*!\brief Obtain slice of the data type at a given index + * + * \details + * Overload to obtain a slice of data at `index`. + * It is typically used in the block to generate BlockSlices or access + * indexed data + * + * \param data Slice to be further sliced + * \param index Index of slicing + */ + static inline auto slice(data_type& data, std::size_t index) + -> reference_type { + return data[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the const data type at a given index + * + * \details + * Overload to obtain a slice of const data at `index`. + * It is typically used in the block to generate ConstBlockSlices or + * access indexed data + * + * \param data Slice to be further sliced + * \param index Index of slicing + */ + static inline auto slice(data_type const& data, std::size_t index) + -> const_reference_type { + return data[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain a sub-slice of the slice at a given index + * + * \details + * Overload to obtain a subslice of slice at `index`. + * It is typically used in the block to generate slices that are then + * filled in by ghost values. + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline auto slice(slice_type& slice, std::size_t index) + -> reference_type { + return slice[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain a sub-slice of the const slice at a given index + * + * \details + * Overload to obtain a slice of data at `index`. + * It is sometimes needed in a Plugin where the context is const, but the + * data of the underlying structure may well be modified. + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline auto slice(slice_type const& slice, std::size_t index) + -> const_reference_type { + return slice[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain sub-slice of the const_slice type at a given index + * + * \details + * Overload to obtain a subslice of const_slice at `index`. + * It is needed in a Plugin where the slice itself is const, such as a + * ConstBlockSlice + * + * \param slice Slice to be further sliced + * \param index Index of slicing + */ + static inline decltype(auto) slice(const_slice_type const& slice, + std::size_t index) { + return slice[index]; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Resize the data type to contain at least `size` entries + * + * \param data The data to be resized + * \param new_size New size of data + */ + static inline void resize(data_type& data, std::size_t new_size) { + ELASTICA_ASSERT(data.size() <= new_size, + "Contract violation, block shrinks"); + return data.resize(new_size); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the data type through a set of indices + * + * \details + * Overload to obtain a slice of `size` amount from data at `start`. + * This is typically used in the block to generate slices that are to be + * filled in by the class hierarchies, when filling in a block + * incrementally. + * + * \param data Data to be sliced + * \param start_index Start index + * \param size Size of the data + */ + static inline auto slice(data_type& data, std::size_t start_index, + std::size_t size) -> slice_type { + return slice_type(&data[start_index], &data[start_index + size]); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Obtain slice of the const data type through a set of indices + * + * \details + * Overload to obtain a slice of `size` amount from const data at `start`. + * This is typically used in the block to generate slices that are to be + * filled in by the class hierarchies, when filling in a block + * incrementally. + * + * \param data Data to be sliced + * \param start_index Start index + * \param size Size of the data + */ + static inline auto slice(data_type const& data, std::size_t start_index, + std::size_t size) -> const_slice_type { + // unaligned always + return const_slice_type(&data[start_index], &data[start_index + size]); + } + //************************************************************************ + + //@} + //************************************************************************ + }; + //************************************************************************** + + } // namespace cosserat_rod + +} // namespace elastica diff --git a/backend/src/Systems/CosseratRods/Traits/Types.hpp b/backend/src/Systems/CosseratRods/Traits/Types.hpp new file mode 100644 index 000000000..f34ca9c75 --- /dev/null +++ b/backend/src/Systems/CosseratRods/Traits/Types.hpp @@ -0,0 +1,35 @@ +//============================================================================== +/*! +// \file +// \brief All trait types belonging to a Cosserat rod +// +// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved +// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved +// +// Distributed under the MIT License. +// See LICENSE.txt for details. +*/ +//============================================================================== + +#pragma once + +namespace elastica { + + namespace cosserat_rod { + + //////////////////////////////////////////////////////////////////////////// + // + // Forward declarations of cosserat rod trait types + // + //////////////////////////////////////////////////////////////////////////// + + //************************************************************************ + /*! \cond ELASTICA_INTERNAL */ + struct DataOpsTraits; + // struct PlacementTrait; + /*! \endcond */ + //************************************************************************ + + } // namespace cosserat_rod + +} // namespace elastica From ecedea500f06e32de6dc93aa7f11435a41a39d96 Mon Sep 17 00:00:00 2001 From: Ankith Date: Sun, 23 Jun 2024 17:37:13 +0530 Subject: [PATCH 010/121] Wrap and test everything in BlazeLinearAlgebra.hpp --- backend/benchmarking/matmul.py | 6 +- backend/src/_linalg.cpp | 271 ++++++++++++++++++++++++++++++++- backend/src/meson.build | 2 +- backend/tests/test_linalg.py | 113 ++++++++++++++ 4 files changed, 388 insertions(+), 4 deletions(-) create mode 100644 backend/tests/test_linalg.py diff --git a/backend/benchmarking/matmul.py b/backend/benchmarking/matmul.py index 3235e0ef6..04e4a2b18 100644 --- a/backend/benchmarking/matmul.py +++ b/backend/benchmarking/matmul.py @@ -1,4 +1,4 @@ -from elasticapp._linalg import batch_matmul_naive, batch_matmul_blaze +from elasticapp._linalg import batch_matmul_naive, batch_matmul_blaze, batch_matmul from elastica._linalg import _batch_matmul import numpy import time @@ -29,14 +29,16 @@ def benchmark_batchsize(funcs: list, batches: list[int], num_iterations: int = 1 results = benchmark_batchsize( - [batch_matmul_naive, batch_matmul_blaze, _batch_matmul], [2**i for i in range(14)] + [batch_matmul_naive, batch_matmul_blaze, batch_matmul, _batch_matmul], [2**i for i in range(14)] ) for size, data in results.items(): pyelastica = data["_batch_matmul"] elasticapp = data["batch_matmul_naive"] elasticapp_blaze = data["batch_matmul_blaze"] + elasticapp_blaze_v2 = data["batch_matmul"] print(f"{size = }") print(f"{pyelastica = }") print(f"{elasticapp = }, ratio: {elasticapp / pyelastica}") print(f"{elasticapp_blaze = }, ratio: {elasticapp_blaze / pyelastica}") + print(f"{elasticapp_blaze_v2 = }, ratio: {elasticapp_blaze_v2 / pyelastica}") print() diff --git a/backend/src/_linalg.cpp b/backend/src/_linalg.cpp index b628ebd2a..651e29177 100644 --- a/backend/src/_linalg.cpp +++ b/backend/src/_linalg.cpp @@ -2,8 +2,12 @@ #include #include +#include "Utilities/Math/BlazeDetail/BlazeLinearAlgebra.hpp" + namespace py = pybind11; +using blaze::DynamicMatrix; +using blaze::DynamicTensor; using blaze::StaticMatrix; /* Simple rewrite of elastica._linalg._batch_matmul */ @@ -75,6 +79,234 @@ py::array_t batch_matmul_blaze( return output_matrix; } +py::array_t difference_kernel(py::array_t vector_batch) +{ + const auto v0 = vector_batch.shape(0); + const auto num_elems = vector_batch.shape(1); + + assert(v0 == 3UL); + auto output_arr = py::array_t{{v0, num_elems - 1}}; + + auto vector_batch_unchecked = vector_batch.unchecked<2>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<2>(); + + DynamicMatrix blaze_vector_batch(v0, num_elems); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_vector_batch(i, j) = vector_batch_unchecked(i, j); + } + } + auto blaze_output = elastica::difference_kernel(blaze_vector_batch); + for (py::ssize_t j = 0; j < num_elems - 1; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + output_arr_unchecked(i, j) = blaze_output(i, j); + } + } + + return output_arr; +} + +py::array_t batch_matvec( + py::array_t matrix_collection, py::array_t vector_collection) +{ + const auto v0 = vector_collection.shape(0); + const auto num_elems = vector_collection.shape(1); + assert(v0 == 3UL); + + assert(matrix_collection.shape(0) == 3UL); + assert(matrix_collection.shape(1) == 3UL); + assert(matrix_collection.shape(2) == num_elems); + + auto output_arr = py::array_t{{v0, num_elems}}; + + auto matrix_collection_unchecked = matrix_collection.unchecked<3>(); + auto vector_collection_unchecked = vector_collection.unchecked<2>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<2>(); + + DynamicTensor blaze_matrix_collection(v0, v0, num_elems); + DynamicMatrix blaze_vector_collection(v0, num_elems); + for (py::ssize_t k = 0; k < num_elems; k++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_matrix_collection(i, j, k) = matrix_collection_unchecked(i, j, k); + } + blaze_vector_collection(j, k) = vector_collection_unchecked(j, k); + } + } + + DynamicMatrix blaze_output(v0, num_elems); + elastica::batch_matvec(blaze_output, blaze_matrix_collection, blaze_vector_collection); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + output_arr_unchecked(i, j) = blaze_output(i, j); + } + } + + return output_arr; +} + +py::array_t batch_matmul( + py::array_t first_matrix_batch, py::array_t second_matrix_batch) +{ + const auto m0 = first_matrix_batch.shape(0); + const auto m1 = first_matrix_batch.shape(1); + const auto num_elems = first_matrix_batch.shape(2); + assert(m0 == 3UL); + assert(m1 == 3UL); + + assert(second_matrix_batch.shape(0) == 3UL); + assert(second_matrix_batch.shape(1) == 3UL); + assert(second_matrix_batch.shape(2) == num_elems); + + auto output_arr = py::array_t{{m0, m1, num_elems}}; + + auto first_matrix_batch_unchecked = first_matrix_batch.unchecked<3>(); + auto second_matrix_batch_unchecked = second_matrix_batch.unchecked<3>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<3>(); + + DynamicTensor blaze_first_matrix_batch(m0, m1, num_elems); + DynamicTensor blaze_second_matrix_batch(m0, m1, num_elems); + for (py::ssize_t k = 0; k < num_elems; k++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_first_matrix_batch(i, j, k) = first_matrix_batch_unchecked(i, j, k); + blaze_second_matrix_batch(i, j, k) = second_matrix_batch_unchecked(i, j, k); + } + } + } + DynamicTensor blaze_output(m0, m1, num_elems); + elastica::batch_matmul(blaze_output, blaze_first_matrix_batch, blaze_second_matrix_batch); + for (py::ssize_t k = 0; k < num_elems; k++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + output_arr_unchecked(i, j, k) = blaze_output(i, j, k); + } + } + } + return output_arr; +} + +py::array_t batch_cross( + py::array_t first_vector_batch, py::array_t second_vector_batch) +{ + const auto v0 = first_vector_batch.shape(0); + const auto num_elems = first_vector_batch.shape(1); + assert(v0 == 3UL); + + assert(second_vector_batch.shape(0) == 3UL); + assert(second_vector_batch.shape(1) == num_elems); + + auto output_arr = py::array_t{{v0, num_elems}}; + + auto first_vector_batch_unchecked = first_vector_batch.unchecked<2>(); + auto second_vector_batch_unchecked = second_vector_batch.unchecked<2>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<2>(); + + DynamicMatrix blaze_first_vector_batch(v0, num_elems); + DynamicMatrix blaze_second_vector_batch(v0, num_elems); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_first_vector_batch(i, j) = first_vector_batch_unchecked(i, j); + blaze_second_vector_batch(i, j) = second_vector_batch_unchecked(i, j); + } + } + + DynamicMatrix blaze_output(v0, num_elems); + elastica::batch_cross(blaze_output, blaze_first_vector_batch, blaze_second_vector_batch); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + output_arr_unchecked(i, j) = blaze_output(i, j); + } + } + + return output_arr; +} + +py::array_t batch_dot( + py::array_t first_vector_batch, py::array_t second_vector_batch) +{ + const auto v0 = first_vector_batch.shape(0); + const auto num_elems = first_vector_batch.shape(1); + assert(v0 == 3UL); + + assert(second_vector_batch.shape(0) == 3UL); + assert(second_vector_batch.shape(1) == num_elems); + + auto output_arr = py::array_t{num_elems}; + + auto first_vector_batch_unchecked = first_vector_batch.unchecked<2>(); + auto second_vector_batch_unchecked = second_vector_batch.unchecked<2>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<1>(); + + DynamicMatrix blaze_first_vector_batch(v0, num_elems); + DynamicMatrix blaze_second_vector_batch(v0, num_elems); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_first_vector_batch(i, j) = first_vector_batch_unchecked(i, j); + blaze_second_vector_batch(i, j) = second_vector_batch_unchecked(i, j); + } + } + + auto blaze_output = elastica::batch_dot(blaze_first_vector_batch, blaze_second_vector_batch); + for (py::ssize_t j = 0; j < num_elems; j++) + { + output_arr_unchecked(j) = blaze_output[j]; + } + + return output_arr; +} + +py::array_t batch_norm(py::array_t vector_batch) +{ + const auto v0 = vector_batch.shape(0); + const auto num_elems = vector_batch.shape(1); + + assert(v0 == 3UL); + + auto output_arr = py::array_t{num_elems}; + + auto vector_batch_unchecked = vector_batch.unchecked<2>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<1>(); + + DynamicMatrix blaze_vector_batch(v0, num_elems); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_vector_batch(i, j) = vector_batch_unchecked(i, j); + } + } + + auto blaze_output = elastica::batch_norm(blaze_vector_batch); + for (py::ssize_t j = 0; j < num_elems; j++) + { + output_arr_unchecked(j) = blaze_output[j]; + } + + return output_arr; +} + PYBIND11_MODULE(_linalg, m) { m.doc() = R"pbdoc( @@ -87,6 +319,13 @@ PYBIND11_MODULE(_linalg, m) :toctree: _generate batch_matmul_naive + batch_matmul_blaze + difference_kernel + batch_matvec + batch_matmul + batch_cross + batch_dot + batch_norm )pbdoc"; m.def("batch_matmul_naive", &batch_matmul_naive, R"pbdoc( @@ -99,9 +338,39 @@ PYBIND11_MODULE(_linalg, m) of 3x3 matrices can be multiplied. )pbdoc"); + m.def("difference_kernel", &difference_kernel, R"pbdoc( + Vector Difference + )pbdoc"); + + m.def("batch_matvec", &batch_matvec, R"pbdoc( + Computes a batchwise matrix-vector product given in indical notation: + matvec_batch{ik} = matrix_batch{ijk} * vector_batch{jk} + )pbdoc"); + + m.def("batch_matmul", &batch_matmul, R"pbdoc( + Computes a batchwise matrix-matrix product given in indical notation: + matmul_batch{ilk} = first_matrix_batch{ijk} * second_matrix_batch{jlk} + )pbdoc"); + + m.def("batch_cross", &batch_cross, R"pbdoc( + Computes a batchwise vector-vector cross product given in indical notation: + cross_batch{il} = LCT{ijk} * first_vector_batch{jl} * second_vector_batch{kl} + where LCT is the Levi-Civita Tensor + )pbdoc"); + + m.def("batch_dot", &batch_dot, R"pbdoc( + Computes a batchwise vector-vector dot product given in indical notation: + dot_batch{j} = first_vector_batch{ij} * second_vector_batch{ij} + )pbdoc"); + + m.def("batch_norm", &batch_norm, R"pbdoc( + Computes a batchwise vector L2 norm given in indical notation: + norm_batch{j} = (vector_batch{ij} * vector_batch{ij})^0.5 + )pbdoc"); + #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); #else m.attr("__version__") = "dev"; #endif -} \ No newline at end of file +} diff --git a/backend/src/meson.build b/backend/src/meson.build index 9fb245448..690dd2722 100644 --- a/backend/src/meson.build +++ b/backend/src/meson.build @@ -10,7 +10,7 @@ example_1 = py.extension_module( _linalg = py.extension_module( '_linalg', sources: ['_linalg.cpp'], - dependencies: [py_dep, pybind11_dep, blaze_dep], + dependencies: [py_dep, pybind11_dep, blaze_dep, blaze_tensor_dep], link_language: 'cpp', install: true, subdir: package, diff --git a/backend/tests/test_linalg.py b/backend/tests/test_linalg.py new file mode 100644 index 000000000..1fba0332b --- /dev/null +++ b/backend/tests/test_linalg.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 + +# This file is based on pyelastica tests/test_math/test_linalg.py + +# System imports +import numpy as np +import pytest +from numpy.testing import assert_allclose +from elasticapp._linalg import ( + difference_kernel, + batch_matvec, + batch_matmul, + batch_cross, + batch_dot, + batch_norm, +) + + +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_difference_kernel(blocksize: int): + input_vector_collection = np.random.randn(3, blocksize) + output_vector_collection = difference_kernel(input_vector_collection) + + correct_vector_collection = ( + input_vector_collection[:, 1:] - input_vector_collection[:, :-1] + ) + + assert_allclose(output_vector_collection, correct_vector_collection) + + +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_batch_matvec(blocksize: int): + input_matrix_collection = np.random.randn(3, 3, blocksize) + input_vector_collection = np.random.randn(3, blocksize) + + test_vector_collection = batch_matvec( + input_matrix_collection, input_vector_collection + ) + + correct_vector_collection = [ + np.dot(input_matrix_collection[..., i], input_vector_collection[..., i]) + for i in range(blocksize) + ] + correct_vector_collection = np.array(correct_vector_collection).T + + assert_allclose(test_vector_collection, correct_vector_collection) + + +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_batch_matmul(blocksize: int): + input_first_matrix_collection = np.random.randn(3, 3, blocksize) + input_second_matrix_collection = np.random.randn(3, 3, blocksize) + + test_matrix_collection = batch_matmul( + input_first_matrix_collection, input_second_matrix_collection + ) + + correct_matrix_collection = np.empty((3, 3, blocksize)) + for i in range(blocksize): + correct_matrix_collection[..., i] = np.dot( + input_first_matrix_collection[..., i], + input_second_matrix_collection[..., i], + ) + + assert_allclose(test_matrix_collection, correct_matrix_collection) + + +# TODO : Generalize to two dimensions +@pytest.mark.parametrize("dim", [3]) +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_batch_cross(dim, blocksize: int): + input_first_vector_collection = np.random.randn(dim, blocksize) + input_second_vector_collection = np.random.randn(dim, blocksize) + + test_vector_collection = batch_cross( + input_first_vector_collection, input_second_vector_collection + ) + correct_vector_collection = np.cross( + input_first_vector_collection, input_second_vector_collection, axisa=0, axisb=0 + ).T + + assert_allclose(test_vector_collection, correct_vector_collection) + + +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_batch_dot(blocksize: int): + input_first_vector_collection = np.random.randn(3, blocksize) + input_second_vector_collection = np.random.randn(3, blocksize) + + test_vector_collection = batch_dot( + input_first_vector_collection, input_second_vector_collection + ) + + correct_vector_collection = np.einsum( + "ij,ij->j", input_first_vector_collection, input_second_vector_collection + ) + + assert_allclose(test_vector_collection, correct_vector_collection) + + +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_batch_norm(blocksize: int): + input_first_vector_collection = np.random.randn(3, blocksize) + + test_vector_collection = batch_norm(input_first_vector_collection) + + correct_vector_collection = np.sqrt( + np.einsum( + "ij,ij->j", input_first_vector_collection, input_first_vector_collection + ) + ) + + assert_allclose(test_vector_collection, correct_vector_collection) From d6128171538356335f15e6ec269ac4f24437deff Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Mon, 24 Jun 2024 14:03:41 -0500 Subject: [PATCH 011/121] python-binding template for vector/matrix/tensor --- .../src/Utilities/Math/Python/Bindings.cpp | 31 +++ .../src/Utilities/Math/Python/BlazeMatrix.cpp | 106 ++++++++ .../src/Utilities/Math/Python/BlazeTensor.cpp | 111 +++++++++ .../src/Utilities/Math/Python/BlazeVector.cpp | 230 ++++++++++++++++++ .../src/Utilities/Math/Python/CMakeLists.txt | 28 +++ .../Utilities/Math/Python/SliceHelpers.hpp | 108 ++++++++ 6 files changed, 614 insertions(+) create mode 100644 backend/src/Utilities/Math/Python/Bindings.cpp create mode 100644 backend/src/Utilities/Math/Python/BlazeMatrix.cpp create mode 100644 backend/src/Utilities/Math/Python/BlazeTensor.cpp create mode 100644 backend/src/Utilities/Math/Python/BlazeVector.cpp create mode 100644 backend/src/Utilities/Math/Python/CMakeLists.txt create mode 100644 backend/src/Utilities/Math/Python/SliceHelpers.hpp diff --git a/backend/src/Utilities/Math/Python/Bindings.cpp b/backend/src/Utilities/Math/Python/Bindings.cpp new file mode 100644 index 000000000..c2ff0a134 --- /dev/null +++ b/backend/src/Utilities/Math/Python/Bindings.cpp @@ -0,0 +1,31 @@ +//****************************************************************************** +// Includes +//****************************************************************************** +#include + +namespace py = pybind11; + +namespace py_bindings { + void bind_blaze_vector(py::module& m); // NOLINT + // void bind_blaze_index_vector(py::module& m); // NOLINT + // void bind_blaze_subvector(py::module& m); // NOLINT + // void bind_blaze_index_subvector(py::module& m); // NOLINT + void bind_blaze_matrix(py::module& m); // NOLINT + // void bind_blaze_submatrix(py::module& m); // NOLINT + void bind_blaze_tensor(py::module& m); // NOLINT + // void bind_blaze_subtensor(py::module& m); // NOLINT +} // namespace py_bindings + +PYBIND11_MODULE(_PyArrays, m) { // NOLINT + m.doc() = R"pbdoc( + Bindings for Elastica++ array types + )pbdoc"; + py_bindings::bind_blaze_vector(m); + // py_bindings::bind_blaze_index_vector(m); + // py_bindings::bind_blaze_subvector(m); + // py_bindings::bind_blaze_index_subvector(m); + py_bindings::bind_blaze_matrix(m); + // py_bindings::bind_blaze_submatrix(m); + py_bindings::bind_blaze_tensor(m); + // py_bindings::bind_blaze_subtensor(m); +} diff --git a/backend/src/Utilities/Math/Python/BlazeMatrix.cpp b/backend/src/Utilities/Math/Python/BlazeMatrix.cpp new file mode 100644 index 000000000..9c3fb654e --- /dev/null +++ b/backend/src/Utilities/Math/Python/BlazeMatrix.cpp @@ -0,0 +1,106 @@ +//****************************************************************************** +// Includes +//****************************************************************************** + +// +// #include "PythonBindings/BoundChecks.hpp" +// +#include "Utilities/DefineTypes.h" +// #include "Utilities/MakeString.hpp" +// +#include "Utilities/Math/Python/SliceHelpers.hpp" +// +#include +#include +#include +#include +#include +#include +// +#include +#include +// +#include + +namespace py = pybind11; + +namespace py_bindings { + + //**************************************************************************** + /*!\brief Helps bind a matrix type in \elastica + * \ingroup python_bindings + */ + void bind_blaze_matrix(py::module& m) { // NOLINT + using Real = ::elastica::real_t; + using type = ::blaze::DynamicMatrix>; + + // Wrapper for basic type operations + py::class_(m, "Matrix", py::buffer_protocol()) + .def(py::init(), py::arg("rows"), + py::arg("columns")) + .def(py::init([](py::buffer buffer) { + py::buffer_info info = buffer.request(); + // Sanity-check the buffer + if (info.format != py::format_descriptor::format()) { + throw std::runtime_error( + "Incompatible format: expected a Real array."); + } + if (info.ndim != 2) { + throw std::runtime_error("Incompatible dimension."); + } + const auto rows = static_cast(info.shape[0]); + const auto columns = static_cast(info.shape[1]); + auto data = static_cast(info.ptr); + return type(rows, columns, data); + }), + py::arg("buffer")) + // Expose the data as a Python buffer so it can be cast into Numpy + // arrays + .def_buffer([](type& matrix) { + return py::buffer_info( + matrix.data(), + // Size of one scalar + sizeof(Real), py::format_descriptor::format(), + // Number of dimensions + 2, + // Size of the buffer + {matrix.rows(), matrix.columns()}, + // Stride for each index (in bytes). Data is stored + // in row-major layout (see `type.hpp`). + {sizeof(Real) * matrix.spacing(), sizeof(Real)}); + }) + .def_property_readonly( + "shape", + +[](const type& self) { + return std::tuple(self.rows(), + self.columns()); + }) + // __getitem__ and __setitem__ are the subscript operators (M[*,*]). + .def( + "__getitem__", + +[](const type& self, + const std::tuple& x) { + // matrix_bounds_check(self, std::get<0>(x), std::get<1>(x)); + return self(std::get<0>(x), std::get<1>(x)); + }) + .def( + "__getitem__", + +[](type& t, std::tuple const slice) { + return array_slice(t, std::move(slice)); + }) + // Need __str__ for converting to string/printing + .def( + "__str__", + +[](const type& self) { return std::string(MakeString{} << self); }) + .def( + "__setitem__", + +[](type& self, const std::tuple& x, + const Real val) { + // matrix_bounds_check(self, std::get<0>(x), std::get<1>(x)); + self(std::get<0>(x), std::get<1>(x)) = val; + }); + } + //**************************************************************************** + +} // namespace py_bindings diff --git a/backend/src/Utilities/Math/Python/BlazeTensor.cpp b/backend/src/Utilities/Math/Python/BlazeTensor.cpp new file mode 100644 index 000000000..57dc73a4d --- /dev/null +++ b/backend/src/Utilities/Math/Python/BlazeTensor.cpp @@ -0,0 +1,111 @@ +//****************************************************************************** +// Includes +//****************************************************************************** + + +// #include "PythonBindings/BoundChecks.hpp" +// +#include "Utilities/DefineTypes.h" +// #include "Utilities/MakeString.hpp" +// +#include "Utilities/Math/Python/SliceHelpers.hpp" +// +#include +#include +#include +#include +#include +#include +// +#include +#include +// +#include + +namespace py = pybind11; + +namespace py_bindings { + + //**************************************************************************** + /*!\brief Helps bind a tensor type in \elastica + * \ingroup python_bindings + */ + void bind_blaze_tensor(py::module& m) { // NOLINT + using Real = ::elastica::real_t; + using type = ::blaze::DynamicTensor; + + // Wrapper for basic type operations + py::class_(m, "Tensor", py::buffer_protocol()) + .def(py::init(), + py::arg("pages"), py::arg("rows"), py::arg("columns")) + .def(py::init([](py::buffer buffer) { + py::buffer_info info = buffer.request(); + // Sanity-check the buffer + if (info.format != py::format_descriptor::format()) { + throw std::runtime_error( + "Incompatible format: expected a Real array."); + } + if (info.ndim != 3) { + throw std::runtime_error("Incompatible dimension."); + } + const auto pages = static_cast(info.shape[0]); + const auto rows = static_cast(info.shape[1]); + const auto columns = static_cast(info.shape[2]); + auto data = static_cast(info.ptr); + return type(pages, rows, columns, data); + }), + py::arg("buffer")) + // Expose the data as a Python buffer so it can be cast into Numpy + // arrays + .def_buffer([](type& tensor) { + return py::buffer_info( + tensor.data(), + // Size of one scalar + sizeof(Real), py::format_descriptor::format(), + // Number of dimensions + 3, + // Size of the buffer + {tensor.pages(), tensor.rows(), tensor.columns()}, + // Stride for each index (in bytes). Data is stored + // in column-major layout (see `type.hpp`). + {sizeof(Real) * tensor.rows() * tensor.spacing(), + sizeof(Real) * tensor.spacing(), sizeof(Real)}); + }) + .def_property_readonly( + "shape", + +[](const type& self) { + return std::tuple( + self.pages(), self.rows(), self.columns()); + }) + // __getitem__ and __setitem__ are the subscript operators (M[*,*]). + .def( + "__getitem__", + +[](const type& self, + const std::tuple& x) { + // tensor_bounds_check(self, std::get<0>(x), std::get<1>(x), + std::get<2>(x)); + return self(std::get<0>(x), std::get<1>(x), std::get<2>(x)); + }) + .def( + "__getitem__", + +[](type& self, std::tuple slice) { + return array_slice(self, std::move(slice)); + }) + .def( + "__setitem__", + +[](type& self, + const std::tuple& x, + const Real val) { + // tensor_bounds_check(self, std::get<0>(x), std::get<1>(x), + std::get<2>(x)); + self(std::get<0>(x), std::get<1>(x), std::get<2>(x)) = val; + }) + // Need __str__ for converting to string/printing + .def( + "__str__", +[](const type& self) { + return std::string(MakeString{} << self); + }); + } + //**************************************************************************** + +} // namespace py_bindings diff --git a/backend/src/Utilities/Math/Python/BlazeVector.cpp b/backend/src/Utilities/Math/Python/BlazeVector.cpp new file mode 100644 index 000000000..adedad39f --- /dev/null +++ b/backend/src/Utilities/Math/Python/BlazeVector.cpp @@ -0,0 +1,230 @@ +//****************************************************************************** +// Includes +//****************************************************************************** + +// #include "PythonBindings/BoundChecks.hpp" +// +#include "Utilities/DefineTypes.h" +// +#include "Utilities/Math/Python/SliceHelpers.hpp" +// +#include +#include +#include +#include +#include +// +#include +#include +#include +// +#include +// + +namespace py = pybind11; + +namespace py_bindings { + + //**************************************************************************** + /*!\brief Helps bind a vector type in \elastica + * \ingroup python_bindings + */ + void bind_blaze_vector(py::module& m) { // NOLINT + + using Real = ::elastica::real_t; + using type = ::blaze::DynamicVector>; + // Wrapper for basic DataVector operations + auto py_vector = + py::class_(m, "Vector", py::buffer_protocol()) + .def(py::init(), py::arg("size")) + .def(py::init(), py::arg("size"), + py::arg("fill")) + .def(py::init([](std::vector const& values) { + type result(values.size()); + std::copy(values.begin(), values.end(), result.begin()); + return result; + }), + py::arg("values")) + .def(py::init([](py::buffer buffer) { + py::buffer_info info = buffer.request(); + // Sanity-check the buffer + if (info.format != py::format_descriptor::format()) { + throw std::runtime_error( + "Incompatible format: expected a Real array"); + } + if (info.ndim != 1) { + throw std::runtime_error("Incompatible dimension."); + } + const auto size = static_cast(info.shape[0]); + auto data = static_cast(info.ptr); + type result(size); + std::copy_n(data, result.size(), result.begin()); + return result; + }), + py::arg("buffer")) + // Expose the data as a Python buffer so it can be cast into Numpy + // arrays + .def_buffer([](type& t) { + return py::buffer_info(t.data(), + // Size of one scalar + sizeof(Real), + py::format_descriptor::format(), + // Number of dimensions + 1, + // Size of the buffer + {t.size()}, + // Stride for each index (in bytes) + {sizeof(Real)}); + }) + .def( + "__iter__", + [](const type& t) { + return py::make_iterator(t.begin(), t.end()); + }, + // Keep object alive while iterator exists + py::keep_alive<0, 1>()) + // __len__ is for being able to write len(my_data_vector) in python + .def("__len__", [](const type& t) { return t.size(); }) + .def_property_readonly( + "shape", + +[](const type& t) { + return std::tuple(t.size()); + }) + // __getitem__ and __setitem__ are the subscript operators + // (operator[]). To define (and overload) operator() use __call__ + .def( + "__getitem__", + +[](const type& t, const size_t i) { + // bounds_check(t, i); + return t[i]; + }) + .def( + "__getitem__", + +[](type& t, const py::slice slice) { + return array_slice(t, std::move(slice)); + }) + .def( + "__setitem__", +[](type& t, const size_t i, const Real v) { + // bounds_check(t, i); + t[i] = v; + }); + + // Need __str__ for converting to string + py_vector + .def("__str__") + // repr allows you to output the object in an interactive python + // terminal using obj to get the "string REPResenting the object". + .def("__repr__") + .def(py::self += py::self) + // Need to do math explicitly converting to DataVector because we don't + // want to represent all the possible expression template types + .def( + "abs", +[](const type& t) { return type{abs(t)}; }) + .def( + "acos", +[](const type& t) { return type{acos(t)}; }) + .def( + "acosh", +[](const type& t) { return type{acosh(t)}; }) + .def( + "asin", +[](const type& t) { return type{asin(t)}; }) + .def( + "asinh", +[](const type& t) { return type{asinh(t)}; }) + .def( + "atan", +[](const type& t) { return type{atan(t)}; }) + .def( + "atan2", + +[](const type& y, const type& x) { return type{atan2(y, x)}; }) + .def( + "atanh", +[](const type& t) { return type{atanh(t)}; }) + .def( + "cbrt", +[](const type& t) { return type{cbrt(t)}; }) + .def( + "cos", +[](const type& t) { return type{cos(t)}; }) + .def( + "cosh", +[](const type& t) { return type{cosh(t)}; }) + .def( + "erf", +[](const type& t) { return type{erf(t)}; }) + .def( + "erfc", +[](const type& t) { return type{erfc(t)}; }) + .def( + "exp", +[](const type& t) { return type{exp(t)}; }) + .def( + "exp2", +[](const type& t) { return type{exp2(t)}; }) + .def( + "exp10", +[](const type& t) { return type{exp10(t)}; }) + // .def( + // "fabs", +[](const type& t) { return type{fabs(t)}; }) + .def( + "hypot", + +[](const type& x, const type& y) { return type{hypot(x, y)}; }) + .def( + "invcbrt", +[](const type& t) { return type{invcbrt(t)}; }) + .def( + "invsqrt", +[](const type& t) { return type{invsqrt(t)}; }) + .def( + "log", +[](const type& t) { return type{log(t)}; }) + .def( + "log2", +[](const type& t) { return type{log2(t)}; }) + .def( + "log10", +[](const type& t) { return type{log10(t)}; }) + .def( + "max", +[](const type& t) { return Real{max(t)}; }) + .def( + "min", +[](const type& t) { return Real{min(t)}; }) + .def( + "pow", + +[](const type& base, double exp) { return type{pow(base, exp)}; }) + .def( + "sin", +[](const type& t) { return type{sin(t)}; }) + .def( + "sinh", +[](const type& t) { return type{sinh(t)}; }) + .def( + "sqrt", +[](const type& t) { return type{sqrt(t)}; }) + .def( + "tan", +[](const type& t) { return type{tan(t)}; }) + .def( + "tanh", +[](const type& t) { return type{tanh(t)}; }) + .def( + "__pow__", +[](const type& base, + const double exp) { return type{pow(base, exp)}; }) + .def( + "__add__", +[](const type& self, + const Real other) { return type{self + other}; }) + .def( + "__radd__", +[](const type& self, + const Real other) { return type{other + self}; }) + .def( + "__sub__", +[](const type& self, + const Real other) { return type{self - other}; }) + .def( + "__rsub__", +[](const type& self, + const Real other) { return type{other - self}; }) + .def( + "__mul__", +[](const type& self, + const Real other) { return type{self * other}; }) + .def( + "__rmul__", +[](const type& self, + const Real other) { return type{other * self}; }) + // Need __div__ for python 2 and __truediv__ for python 3. + .def( + "__div__", +[](const type& self, + const Real other) { return type{self / other}; }) + .def( + "__truediv__", +[](const type& self, + const Real other) { return type{self / other}; }) + .def( + "__rdiv__", +[](const type& self, + const Real other) { return type{other / self}; }) + .def( + "__rtruediv__", + +[](const type& self, const Real other) { + return type{other / self}; + }) + .def(py::self == py::self) + .def(py::self != py::self) + .def( + "__neg__", +[](const type& t) { return type{-t}; }); + } + //**************************************************************************** + +} // namespace py_bindings diff --git a/backend/src/Utilities/Math/Python/CMakeLists.txt b/backend/src/Utilities/Math/Python/CMakeLists.txt new file mode 100644 index 000000000..970d41e9c --- /dev/null +++ b/backend/src/Utilities/Math/Python/CMakeLists.txt @@ -0,0 +1,28 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. + +set(LIBRARY "PyArrays") + +elastica_python_add_module( + Arrays + LIBRARY_NAME ${LIBRARY} + SOURCES + Bindings.cpp + BlazeVector.cpp + # BlazeSubvector.cpp + # BlazeIndexVector.cpp + # BlazeIndexSubVector.cpp + BlazeMatrix.cpp + # BlazeSubmatrix.cpp + BlazeTensor.cpp + # BlazeSubtensor.cpp +) + +elastica_python_link_libraries( + ${LIBRARY} + PRIVATE + Blaze + BlazeTensor + pybind11::module + Utilities +) diff --git a/backend/src/Utilities/Math/Python/SliceHelpers.hpp b/backend/src/Utilities/Math/Python/SliceHelpers.hpp new file mode 100644 index 000000000..a810a9938 --- /dev/null +++ b/backend/src/Utilities/Math/Python/SliceHelpers.hpp @@ -0,0 +1,108 @@ +//****************************************************************************** +// Includes +//****************************************************************************** + +#pragma once +// +// #include "Utilities/MakeString.hpp" +// +#include +#include +#include +#include +#include +#include +// +#include +// +#include +#include +#include +#include + +namespace py_bindings { + + struct SliceInfo { + //! Start of slice + std::size_t start; + //! Length of slice + std::size_t slicelength; + }; + + //**************************************************************************** + /*!\brief Checks validitity of slice along Axis, raises error if invalid + * \ingroup python_bindings + */ + template + auto check_slice(const std::size_t limit, pybind11::slice const slice) { + std::size_t start, stop, step, slicelength; + if (!slice.compute(limit, &start, &stop, &step, &slicelength)) + throw pybind11::error_already_set(); + if (step != 1) + throw std::runtime_error(std::string( + "step !=1 unsupported along axis " << Axis)); + + return SliceInfo{start, slicelength}; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Takes 1D slices of an array + * \ingroup python_bindings + */ + template + auto array_slice(T& t, pybind11::slice slice) { + constexpr std::size_t axis = 0UL; + auto slice_info = check_slice(t.size(), std::move(slice)); + return ::blaze::subvector(t, slice_info.start, slice_info.slicelength); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Takes 2D slices of an array + * \ingroup python_bindings + */ + template + auto array_slice(T& t, std::tuple slices) { + constexpr std::size_t row_slice = 0UL; + constexpr std::size_t col_slice = 1UL; + auto slice_info = std::make_tuple( + check_slice(t.rows(), + std::get(std::move(slices))), + check_slice(t.columns(), + std::get(std::move(slices)))); + return ::blaze::submatrix(t, std::get(slice_info).start, + std::get(slice_info).start, + std::get(slice_info).slicelength, + std::get(slice_info).slicelength); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Takes 3D slices of an array + * \ingroup python_bindings + */ + template + auto array_slice( + T& t, + std::tuple slices) { + constexpr std::size_t page_slice = 0UL; + constexpr std::size_t row_slice = 1UL; + constexpr std::size_t col_slice = 2UL; + auto slice_info = std::make_tuple( + check_slice(t.pages(), + std::get(std::move(slices))), + check_slice(t.rows(), + std::get(std::move(slices))), + check_slice(t.columns(), + std::get(std::move(slices)))); + return ::blaze::subtensor(t, std::get(slice_info).start, + std::get(slice_info).start, + std::get(slice_info).start, + std::get(slice_info).slicelength, + std::get(slice_info).slicelength, + std::get(slice_info).slicelength); + } + //**************************************************************************** + +} // namespace py_bindings From fb7f32d55abb1df98980e7988d87811913ba6726 Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Wed, 26 Jun 2024 09:51:58 -0500 Subject: [PATCH 012/121] include SO3 backend implementation --- .../Systems/States/Expressions/backends.hpp | 14 ++ .../Expressions/backends/CMakeLists.txt | 27 +++ .../Expressions/backends/Declarations.hpp | 27 +++ .../States/Expressions/backends/README.md | 5 + .../States/Expressions/backends/blaze.hpp | 11 ++ .../Expressions/backends/blaze/CMakeLists.txt | 39 ++++ .../Expressions/backends/blaze/Resize.hpp | 46 +++++ .../backends/blaze/SO3PrimitiveAddAssign.hpp | 38 ++++ .../SO3PrimitiveAddAssign/BaseTemplate.hpp | 32 ++++ .../blaze/SO3PrimitiveAddAssign/SIMD.hpp | 181 ++++++++++++++++++ .../blaze/SO3PrimitiveAddAssign/Scalar.hpp | 108 +++++++++++ .../backends/blaze/SO3PrimitiveAssign.hpp | 58 ++++++ .../Expressions/backends/blaze/Size.hpp | 38 ++++ 13 files changed, 624 insertions(+) create mode 100644 backend/src/Systems/States/Expressions/backends.hpp create mode 100644 backend/src/Systems/States/Expressions/backends/CMakeLists.txt create mode 100644 backend/src/Systems/States/Expressions/backends/Declarations.hpp create mode 100644 backend/src/Systems/States/Expressions/backends/README.md create mode 100644 backend/src/Systems/States/Expressions/backends/blaze.hpp create mode 100644 backend/src/Systems/States/Expressions/backends/blaze/CMakeLists.txt create mode 100644 backend/src/Systems/States/Expressions/backends/blaze/Resize.hpp create mode 100644 backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign.hpp create mode 100644 backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/BaseTemplate.hpp create mode 100644 backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/SIMD.hpp create mode 100644 backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/Scalar.hpp create mode 100644 backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAssign.hpp create mode 100644 backend/src/Systems/States/Expressions/backends/blaze/Size.hpp diff --git a/backend/src/Systems/States/Expressions/backends.hpp b/backend/src/Systems/States/Expressions/backends.hpp new file mode 100644 index 000000000..97f39d78e --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends.hpp @@ -0,0 +1,14 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +/// +#include "Systems/States/Expressions/backends/Declarations.hpp" +/// + +#define ELASTICA_USE_BLAZE 1 // todo : from CMAKE + +#if defined(ELASTICA_USE_BLAZE) +#include "Systems/States/Expressions/backends/blaze.hpp" +#endif // ELASTICA_USE_BLAZE diff --git a/backend/src/Systems/States/Expressions/backends/CMakeLists.txt b/backend/src/Systems/States/Expressions/backends/CMakeLists.txt new file mode 100644 index 000000000..c50e31671 --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/CMakeLists.txt @@ -0,0 +1,27 @@ +# Distributed under the MIT License. See LICENSE.txt for details. + +set(LIBRARY StateExpressionBackends) + +add_elastica_library(${LIBRARY} INTERFACE) + +elastica_target_headers( + ${LIBRARY} + INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/elastica + blaze.hpp + Declarations.hpp +) + +elastica_target_sources( + ${LIBRARY} + INTERFACE + blaze.hpp + Declarations.hpp +) + +add_subdirectory(blaze) + +target_link_libraries( + ${LIBRARY} + INTERFACE + SO3PrimitiveAddAssignKernel +) diff --git a/backend/src/Systems/States/Expressions/backends/Declarations.hpp b/backend/src/Systems/States/Expressions/backends/Declarations.hpp new file mode 100644 index 000000000..1a545acd0 --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/Declarations.hpp @@ -0,0 +1,27 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include // std::size_t + +namespace elastica { + + namespace states { + + // assign version doesnt care about vectorization + template + auto SO3_primitive_assign(LHSMatrix& lhs, RHSMatrix const& rhs_matrix, + RHSVector const& rhs_vector) noexcept -> void; + // add assign version doesnt care about vectorization + template + auto SO3_primitive_add_assign(LHSMatrix& lhs, + RHSVector const& rhs_vector) noexcept -> void; + template + inline auto size_backend(Type const&) noexcept -> std::size_t; + template + inline auto resize_backend(Type&, std::size_t) -> void; + + } // namespace states + +} // namespace elastica diff --git a/backend/src/Systems/States/Expressions/backends/README.md b/backend/src/Systems/States/Expressions/backends/README.md new file mode 100644 index 000000000..6d2d95092 --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/README.md @@ -0,0 +1,5 @@ +## backends + +To interface the requirements of SO3 and SE3 templates with any custom type that the user provides, we need a couple of +interface functions to connect the frontend (state templates) to the backend data type. This folder contains all such +interface functions for commonly used elastica types. diff --git a/backend/src/Systems/States/Expressions/backends/blaze.hpp b/backend/src/Systems/States/Expressions/backends/blaze.hpp new file mode 100644 index 000000000..221763f9a --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/blaze.hpp @@ -0,0 +1,11 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/States/Expressions/backends/Declarations.hpp" +/// +#include "Systems/States/Expressions/backends/blaze/Resize.hpp" +#include "Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign.hpp" +#include "Systems/States/Expressions/backends/blaze/SO3PrimitiveAssign.hpp" +#include "Systems/States/Expressions/backends/blaze/Size.hpp" diff --git a/backend/src/Systems/States/Expressions/backends/blaze/CMakeLists.txt b/backend/src/Systems/States/Expressions/backends/blaze/CMakeLists.txt new file mode 100644 index 000000000..b0abc964c --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/blaze/CMakeLists.txt @@ -0,0 +1,39 @@ +# Distributed under the MIT License. See LICENSE.txt for details. + +set(LIBRARY SO3PrimitiveAddAssignKernel) + +add_elastica_library(${LIBRARY} INTERFACE) + +elastica_target_headers( + ${LIBRARY} + INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/elastica + SO3PrimitiveAddAssign/BaseTemplate.hpp + SO3PrimitiveAddAssign/Scalar.hpp + SO3PrimitiveAddAssign/SIMD.hpp + Resize.hpp + Size.hpp + SO3PrimitiveAddAssign.hpp + SO3PrimitiveAssign.hpp +) + +elastica_target_sources( + ${LIBRARY} + INTERFACE + SO3PrimitiveAddAssign/BaseTemplate.hpp + SO3PrimitiveAddAssign/Scalar.hpp + SO3PrimitiveAddAssign/SIMD.hpp + Resize.hpp + Size.hpp + SO3PrimitiveAddAssign.hpp + SO3PrimitiveAssign.hpp +) + + +target_link_libraries( + ${LIBRARY} + INTERFACE + Utilities + Blaze + Frames + ModuleSettings +) diff --git a/backend/src/Systems/States/Expressions/backends/blaze/Resize.hpp b/backend/src/Systems/States/Expressions/backends/blaze/Resize.hpp new file mode 100644 index 000000000..da2a7bc50 --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/blaze/Resize.hpp @@ -0,0 +1,46 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include // std::size_t + +#include +#include + +// +// this comes from the simulator module, which seems like an anti-pattern +// should be in Systems instead +#include "Simulator/Frames.hpp" +// +#include "Systems/States/Expressions/backends/Declarations.hpp" + +namespace elastica { + + namespace states { + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + // the parts below are repeated from the Block module, but they are almost + // always never used in the states module since the size is always expected + // to be + template + inline auto resize_backend(blaze::DynamicTensor& data, + std::size_t new_size) -> void { + return data.resize(Frames::Dimension, Frames::Dimension, new_size, true); + } + + template // Type tag + inline auto resize_backend(blaze::DynamicMatrix& data, + std::size_t new_size) -> void { + return data.resize(Frames::Dimension, new_size, true); + } + /*! \endcond */ + //************************************************************************** + + } // namespace states + +} // namespace elastica diff --git a/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign.hpp b/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign.hpp new file mode 100644 index 000000000..35d6d3fa7 --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign.hpp @@ -0,0 +1,38 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +// +#include "Systems/States/Expressions/backends/Declarations.hpp" +#include "Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/BaseTemplate.hpp" +#include "Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/SIMD.hpp" +#include "Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/Scalar.hpp" +#include "Systems/States/Expressions/backends/blaze/SO3PrimitiveAssign.hpp" +#include "Systems/States/Expressions/backends/blaze/Size.hpp" + +namespace elastica { + + namespace states { + + template + auto SO3_primitive_add_assign(blaze::DynamicTensor& lhs_matrix_batch, + RHSVectorBatch const& rhs_vector) noexcept + -> void { + /* + * This version creates memory every time + auto temp_to_synch(lhs_matrix_batch); // TODO: avoid making new memory + // (but not sure how) + // The rotation computation cannot be computed asynchronously during + // add_assign. + SO3_primitive_assign(lhs_matrix_batch, temp_to_synch, rhs_vector); + */ + + detail::SO3AddAssign()>:: + apply(lhs_matrix_batch, rhs_vector); + } + + } // namespace states + +} // namespace elastica diff --git a/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/BaseTemplate.hpp b/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/BaseTemplate.hpp new file mode 100644 index 000000000..91df12873 --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/BaseTemplate.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "Configuration/Kernels.hpp" +#include "ModuleSettings/Vectorization.hpp" + +namespace elastica { + + namespace states { + + namespace detail { + + enum class SO3AddAssignKind { scalar, simd }; + + template + struct SO3AddAssign; + + template + constexpr auto backend_choice() -> BackendEnumKind; + + template <> + constexpr auto backend_choice() -> SO3AddAssignKind { + return ELASTICA_USE_VECTORIZATION + ? SO3AddAssignKind:: + ELASTICA_COSSERATROD_LIB_SO3_ADDITION_BACKEND + : SO3AddAssignKind::scalar; + } + + } // namespace detail + + } // namespace states + +} // namespace elastica diff --git a/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/SIMD.hpp b/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/SIMD.hpp new file mode 100644 index 000000000..bb8c04ed5 --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/SIMD.hpp @@ -0,0 +1,181 @@ +#pragma once + +#include +#include +// +#include +#include // for padding +// +#include +#include +// +#include "Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/BaseTemplate.hpp" +#include "Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/Scalar.hpp" +#include "Systems/States/Expressions/backends/blaze/Size.hpp" +// +#include "ErrorHandling/Assert.hpp" +// +#include "Utilities/Unroll.hpp" + +namespace elastica { + + namespace states { + + namespace detail { + + template <> + struct SO3AddAssign { + template + static auto apply(blaze::DynamicTensor& lhs_matrix_batch, + RHSVectorBatch const& rhs_vector_batch) noexcept + -> void { + using ::elastica::states::size_backend; + const std::size_t n_dofs = size_backend(lhs_matrix_batch); + constexpr bool remainder(!::blaze::usePadding || + !blaze::IsPadded_v); + constexpr std::size_t SIMDSIZE(blaze::SIMDTrait::size); + + const std::size_t i_dof_pos( + remainder ? blaze::prevMultiple(n_dofs, SIMDSIZE) : n_dofs); + ELASTICA_ASSERT(i_dof_pos <= n_dofs, "Invalid end calculation"); + + // first do loops of size simd width + + // begin has (row, page) as the syntax + typename blaze::DynamicTensor::Iterator q_it[9UL] = { + lhs_matrix_batch.begin(0UL, 0UL), + lhs_matrix_batch.begin(1UL, 0UL), + lhs_matrix_batch.begin(2UL, 0UL), + lhs_matrix_batch.begin(0UL, 1UL), + lhs_matrix_batch.begin(1UL, 1UL), + lhs_matrix_batch.begin(2UL, 1UL), + lhs_matrix_batch.begin(0UL, 2UL), + lhs_matrix_batch.begin(1UL, 2UL), + lhs_matrix_batch.begin(2UL, 2UL)}; + + typename RHSVectorBatch::ConstIterator u_it[3UL] = { + rhs_vector_batch.begin(0UL), rhs_vector_batch.begin(1UL), + rhs_vector_batch.begin(2UL)}; + // + using SIMDType = typename blaze::DynamicTensor::SIMDType; + + // decide how much to unroll here + // unrolling more probably wont help because of register pressure + SIMDType q[9UL]; + SIMDType u[3UL]; + SIMDType c_alpha, alpha, beta; + +#if !(BLAZE_SVML_MODE || BLAZE_SLEEF_MODE) + // a temporary aligned register to pipe contents into + alignas(::blaze::AlignmentOf_v) T temp[SIMDSIZE]; +#endif + + std::size_t i_dof = 0UL; + + for (; i_dof < i_dof_pos; i_dof += SIMDSIZE) { + { + // 1. load data into registers + + // 1.1 load u + UNROLL_LOOP(3UL) + for (std::size_t dim = 0UL; dim < 3UL; ++dim) { + u[dim] = u_it[dim].load(); + } + + // 1.2 load q + UNROLL_LOOP(9UL) + for (std::size_t dim = 0UL; dim < 9UL; ++dim) { + q[dim] = q_it[dim].load(); + } + } + + { + // 2. compute the angle of rotation + // blaze dispatches to std + beta = ::blaze::sqrt(u[0UL] * u[0UL] + u[1UL] * u[1UL] + + u[2UL] * u[2UL]); + + // 3. normalize the axis of rotation + /*Clang-Tidy: Use range-based for loop instead*/ + UNROLL_LOOP(3UL) + for (std::size_t dim = 0UL; dim < 3UL; ++dim) { /* NOLINT */ + // bad access pattern + // TODO : refactor magic number + auto arg = (beta + ::blaze::set(T(1e-14))); + u[dim] /= arg; + } + + // compute trigonometric entities +#if BLAZE_SVML_MODE || BLAZE_SLEEF_MODE + c_alpha = ::blaze::cos(beta); + beta = ::blaze::sin(beta); +#else + // no intrinsics, so we unpack the SIMD vector, compute sin and + // cos on each via regular math functions and then repack it +#pragma unroll SIMDSIZE + for (std::size_t simd_idx = 0UL; simd_idx < SIMDSIZE; ++simd_idx) + temp[simd_idx] = ::blaze::cos(beta[simd_idx]); + + c_alpha = ::blaze::loada(temp); + +#pragma unroll SIMDSIZE + for (std::size_t simd_idx = 0UL; simd_idx < SIMDSIZE; ++simd_idx) + temp[simd_idx] = ::blaze::sin(beta[simd_idx]); + + beta = ::blaze::loada(temp); +#endif + alpha = ::blaze::set(T(1.0)) - c_alpha; + } + + { + // 4. compute the rotated batch + /*Clang-Tidy: Use range-based for loop instead*/ + UNROLL_LOOP(3UL) + for (std::size_t j_dim = 0UL; j_dim < 3UL; ++j_dim) { /* NOLINT */ + // Force evaluate it + const auto com = (q[j_dim] * u[0UL] + q[3UL + j_dim] * u[1UL] + + q[6UL + j_dim] * u[2UL]) + .eval(); + + q_it[0UL + j_dim].stream( + c_alpha * q[j_dim] + alpha * u[0UL] * com + + beta * (q[3UL + j_dim] * u[2UL] - q[6UL + j_dim] * u[1UL])); + + q_it[3UL + j_dim].stream( + c_alpha * q[3UL + j_dim] + alpha * u[1UL] * com + + beta * (q[6UL + j_dim] * u[0UL] - q[j_dim] * u[2UL])); + + q_it[6UL + j_dim].stream( + c_alpha * q[6UL + j_dim] + alpha * u[2UL] * com + + beta * (q[j_dim] * u[1UL] - q[3UL + j_dim] * u[0UL])); + + } // jdim + } + + { + // 5. advance all the iterators to the next SIMD lane + /*Clang-Tidy: Use range-based for loop instead*/ + UNROLL_LOOP(3UL) + for (std::size_t dim = 0UL; dim < 3UL; ++dim) /* NOLINT */ + u_it[dim] += SIMDSIZE; + + UNROLL_LOOP(9UL) + for (std::size_t dim = 0UL; dim < 9UL; ++dim) /* NOLINT */ + q_it[dim] += SIMDSIZE; + } + + } // i_dof + + // then do the last loops, peeling them off to serial scalar + // implementation UNTESTED + if (remainder) + SO3AddAssign::internal_apply( + lhs_matrix_batch, rhs_vector_batch, i_dof, n_dofs); + } + }; + + } // namespace detail + + } // namespace states + +} // namespace elastica diff --git a/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/Scalar.hpp b/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/Scalar.hpp new file mode 100644 index 000000000..6d9e76223 --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/Scalar.hpp @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include + +#include "Systems/States/Expressions/backends/blaze/SO3PrimitiveAddAssign/BaseTemplate.hpp" +#include "Systems/States/Expressions/backends/blaze/Size.hpp" +// +#include "Utilities/Unroll.hpp" + +namespace elastica { + + namespace states { + + namespace detail { + + template <> + struct SO3AddAssign { + template + static auto internal_apply(blaze::DynamicTensor& lhs_matrix_batch, + RHSVectorBatch const& rhs_vector_batch, + const std::size_t index_start, + const std::size_t index_fin) noexcept + -> void { + // 0. allocate memory for rolling window + T q[9UL]; + /* + * structure is + * q0 q3 q6 + * q1 q4 q7 + * q2 q5 q8 + */ + T u[3UL]; + // T theta, alpha, beta; // doesnt matter the extra memory + + for (std::size_t i_dof = index_start; i_dof < index_fin; ++i_dof) { + // 1. load data into registers + UNROLL_LOOP(3UL) + for (std::size_t dim = 0UL; dim < 3UL; ++dim) { + // bad access pattern + u[dim] = rhs_vector_batch(dim, i_dof); + } + + UNROLL_LOOP(3UL) // Needs to be timed + for (std::size_t k_dim = 0UL; k_dim < 3UL; ++k_dim) { + UNROLL_LOOP(3UL) + for (std::size_t j_dim = 0UL; j_dim < 3UL; ++j_dim) { + // bad access pattern + // convention according to SO3PrimitiveAssign + q[3UL * k_dim + j_dim] = lhs_matrix_batch(k_dim, j_dim, i_dof); + } + } + + // Now work only with registers + + // 2. compute the angle of rotation + // blaze dispatches to std + const T theta = ::std::sqrt(u[0UL] * u[0UL] + u[1UL] * u[1UL] + + u[2UL] * u[2UL]); + const T c_alpha = ::std::cos(theta); + const T alpha = T(1.0) - c_alpha; + const T beta = ::std::sin(theta); + + // 3. normalize the axis of rotation + /*Clang-Tidy: Use range-based for loop instead*/ + UNROLL_LOOP(3UL) + for (std::size_t dim = 0UL; dim < 3UL; ++dim) { /* NOLINT */ + // bad access pattern + u[dim] /= (theta + 1e-14); // TODO : refactor magic number + } + + /*Clang-Tidy: Use range-based for loop instead*/ + UNROLL_LOOP(3UL) + for (std::size_t j_dim = 0UL; j_dim < 3UL; ++j_dim) { /* NOLINT */ + const T com = q[j_dim] * u[0UL] + q[3UL + j_dim] * u[1UL] + + q[6UL + j_dim] * u[2UL]; + + lhs_matrix_batch(0UL, j_dim, i_dof) = + c_alpha * q[j_dim] + alpha * u[0UL] * com + + beta * (q[3UL + j_dim] * u[2UL] - q[6UL + j_dim] * u[1UL]); + + lhs_matrix_batch(1UL, j_dim, i_dof) = + c_alpha * q[3UL + j_dim] + alpha * u[1UL] * com + + beta * (q[6UL + j_dim] * u[0UL] - q[j_dim] * u[2UL]); + + lhs_matrix_batch(2UL, j_dim, i_dof) = + c_alpha * q[6UL + j_dim] + alpha * u[2UL] * com + + beta * (q[j_dim] * u[1UL] - q[3UL + j_dim] * u[0UL]); + } // j_dim + } // i_dof + } + + template + static inline auto apply( + blaze::DynamicTensor& lhs_matrix_batch, + RHSVectorBatch const& rhs_vector_batch) noexcept -> void { + using ::elastica::states::size_backend; + internal_apply(lhs_matrix_batch, rhs_vector_batch, 0UL, + size_backend(lhs_matrix_batch)); + } + }; + + } // namespace detail + + } // namespace states + +} // namespace elastica diff --git a/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAssign.hpp b/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAssign.hpp new file mode 100644 index 000000000..a103f1168 --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/blaze/SO3PrimitiveAssign.hpp @@ -0,0 +1,58 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +#include "Systems/States/Expressions/backends/Declarations.hpp" +#include "Utilities/Math/BlazeDetail/BlazeRotation.hpp" + +namespace elastica { + + namespace states { + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + // clang-format off + template + auto SO3_primitive_assign(blaze::DynamicTensor& lhs_matrix_batch, + blaze::DynamicTensor const& rhs_matrix_batch, + RHSVectorBatch const& vector_batch) noexcept // vector bacth includes (omega*dt). Must include minus sign here + -> void { +// using ::elastica::exp_batch; +// exp_batch(lhs_matrix_batch, -vector_batch); +// lhs_matrix_batch = lhs_matrix_batch * rhs_matrix_batch; + // TODO: Double check. Place the function externally. Double check the sign + auto theta = blaze::sqrt(blaze::sum(vector_batch % vector_batch)); + auto alpha = 1.0 - blaze::cos(theta); + auto beta = blaze::sin(theta); + auto u0 = blaze::row(vector_batch, 0UL) / (theta+1e-14); + auto u1 = blaze::row(vector_batch, 1UL) / (theta+1e-14); + auto u2 = blaze::row(vector_batch, 2UL) / (theta+1e-14); + auto q0 = blaze::row(blaze::pageslice(rhs_matrix_batch, 0UL), 0UL); + auto q1 = blaze::row(blaze::pageslice(rhs_matrix_batch, 0UL), 1UL); + auto q2 = blaze::row(blaze::pageslice(rhs_matrix_batch, 0UL), 2UL); + auto q3 = blaze::row(blaze::pageslice(rhs_matrix_batch, 1UL), 0UL); + auto q4 = blaze::row(blaze::pageslice(rhs_matrix_batch, 1UL), 1UL); + auto q5 = blaze::row(blaze::pageslice(rhs_matrix_batch, 1UL), 2UL); + auto q6 = blaze::row(blaze::pageslice(rhs_matrix_batch, 2UL), 0UL); + auto q7 = blaze::row(blaze::pageslice(rhs_matrix_batch, 2UL), 1UL); + auto q8 = blaze::row(blaze::pageslice(rhs_matrix_batch, 2UL), 2UL); + // TODO: maybe replace blaze::pow(x,2) to x % x + blaze::row(blaze::pageslice(lhs_matrix_batch, 0UL), 0UL) = alpha*((-blaze::pow(u1, 2) - blaze::pow(u2, 2))*q0 + q3*u0*u1 + q6*u0*u2) + beta*( q3*u2 - q6*u1) + q0; + blaze::row(blaze::pageslice(lhs_matrix_batch, 0UL), 1UL) = alpha*((-blaze::pow(u1, 2) - blaze::pow(u2, 2))*q1 + q4*u0*u1 + q7*u0*u2) + beta*( q4*u2 - q7*u1) + q1; + blaze::row(blaze::pageslice(lhs_matrix_batch, 0UL), 2UL) = alpha*((-blaze::pow(u1, 2) - blaze::pow(u2, 2))*q2 + q5*u0*u1 + q8*u0*u2) + beta*( q5*u2 - q8*u1) + q2; + blaze::row(blaze::pageslice(lhs_matrix_batch, 1UL), 0UL) = alpha*((-blaze::pow(u0, 2) - blaze::pow(u2, 2))*q3 + q0*u0*u1 + q6*u1*u2) + beta*(-q0*u2 + q6*u0) + q3; + blaze::row(blaze::pageslice(lhs_matrix_batch, 1UL), 1UL) = alpha*((-blaze::pow(u0, 2) - blaze::pow(u2, 2))*q4 + q1*u0*u1 + q7*u1*u2) + beta*(-q1*u2 + q7*u0) + q4; + blaze::row(blaze::pageslice(lhs_matrix_batch, 1UL), 2UL) = alpha*((-blaze::pow(u0, 2) - blaze::pow(u2, 2))*q5 + q2*u0*u1 + q8*u1*u2) + beta*(-q2*u2 + q8*u0) + q5; + blaze::row(blaze::pageslice(lhs_matrix_batch, 2UL), 0UL) = alpha*((-blaze::pow(u0, 2) - blaze::pow(u1, 2))*q6 + q0*u0*u2 + q3*u1*u2) + beta*( q0*u1 - q3*u0) + q6; + blaze::row(blaze::pageslice(lhs_matrix_batch, 2UL), 1UL) = alpha*((-blaze::pow(u0, 2) - blaze::pow(u1, 2))*q7 + q1*u0*u2 + q4*u1*u2) + beta*( q1*u1 - q4*u0) + q7; + blaze::row(blaze::pageslice(lhs_matrix_batch, 2UL), 2UL) = alpha*((-blaze::pow(u0, 2) - blaze::pow(u1, 2))*q8 + q2*u0*u2 + q5*u1*u2) + beta*( q2*u1 - q5*u0) + q8; + } + // clang-format on + + /*! \endcond */ + //************************************************************************** + } + +} diff --git a/backend/src/Systems/States/Expressions/backends/blaze/Size.hpp b/backend/src/Systems/States/Expressions/backends/blaze/Size.hpp new file mode 100644 index 000000000..a9b930039 --- /dev/null +++ b/backend/src/Systems/States/Expressions/backends/blaze/Size.hpp @@ -0,0 +1,38 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +#include +#include // std::size_t + +#include "Systems/States/Expressions/backends/Declarations.hpp" + +namespace elastica { + + namespace states { + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + template + inline auto size_backend( + blaze::DynamicTensor const& tensor_batch) noexcept -> std::size_t { + return tensor_batch.columns(); + } + + template // Type tag + inline auto size_backend( + blaze::DynamicMatrix const& matrix_batch) noexcept + -> std::size_t { + return matrix_batch.columns(); + } + /*! \endcond */ + //************************************************************************** + + } // namespace states + +} // namespace elastica From f66d0502bda668c3ed70e58bb6d1b3759ea722fe Mon Sep 17 00:00:00 2001 From: Ankith Date: Fri, 5 Jul 2024 14:46:17 +0530 Subject: [PATCH 013/121] Fix Utilities/Math/Python issues and make it build --- backend/meson.build | 2 +- backend/src/Utilities/Math/Python/BlazeMatrix.cpp | 6 +++--- backend/src/Utilities/Math/Python/BlazeTensor.cpp | 14 +++++++------- backend/src/Utilities/Math/Python/BlazeVector.cpp | 6 +++--- backend/src/Utilities/Math/Python/SliceHelpers.hpp | 2 +- backend/src/meson.build | 14 ++++++++++++++ 6 files changed, 29 insertions(+), 15 deletions(-) diff --git a/backend/meson.build b/backend/meson.build index e9a1d4650..4c5cce8f2 100644 --- a/backend/meson.build +++ b/backend/meson.build @@ -2,7 +2,7 @@ project( 'elasticapp', ['cpp'], version: '0.0.3', - default_options: ['cpp_std=c++17'], + default_options: ['cpp_std=c++14'], ) package = 'elasticapp' diff --git a/backend/src/Utilities/Math/Python/BlazeMatrix.cpp b/backend/src/Utilities/Math/Python/BlazeMatrix.cpp index 9c3fb654e..56f55a110 100644 --- a/backend/src/Utilities/Math/Python/BlazeMatrix.cpp +++ b/backend/src/Utilities/Math/Python/BlazeMatrix.cpp @@ -90,9 +90,9 @@ namespace py_bindings { return array_slice(t, std::move(slice)); }) // Need __str__ for converting to string/printing - .def( - "__str__", - +[](const type& self) { return std::string(MakeString{} << self); }) + // .def( + // "__str__", + // +[](const type& self) { return std::string(MakeString{} << self); }) .def( "__setitem__", +[](type& self, const std::tuple& x, diff --git a/backend/src/Utilities/Math/Python/BlazeTensor.cpp b/backend/src/Utilities/Math/Python/BlazeTensor.cpp index 57dc73a4d..156f7f891 100644 --- a/backend/src/Utilities/Math/Python/BlazeTensor.cpp +++ b/backend/src/Utilities/Math/Python/BlazeTensor.cpp @@ -83,7 +83,7 @@ namespace py_bindings { +[](const type& self, const std::tuple& x) { // tensor_bounds_check(self, std::get<0>(x), std::get<1>(x), - std::get<2>(x)); + // std::get<2>(x)); return self(std::get<0>(x), std::get<1>(x), std::get<2>(x)); }) .def( @@ -97,14 +97,14 @@ namespace py_bindings { const std::tuple& x, const Real val) { // tensor_bounds_check(self, std::get<0>(x), std::get<1>(x), - std::get<2>(x)); + // std::get<2>(x)); self(std::get<0>(x), std::get<1>(x), std::get<2>(x)) = val; - }) - // Need __str__ for converting to string/printing - .def( - "__str__", +[](const type& self) { - return std::string(MakeString{} << self); }); + // Need __str__ for converting to string/printing + // .def( + // "__str__", +[](const type& self) { + // return std::string(MakeString{} << self); + // }); } //**************************************************************************** diff --git a/backend/src/Utilities/Math/Python/BlazeVector.cpp b/backend/src/Utilities/Math/Python/BlazeVector.cpp index adedad39f..86239102f 100644 --- a/backend/src/Utilities/Math/Python/BlazeVector.cpp +++ b/backend/src/Utilities/Math/Python/BlazeVector.cpp @@ -112,11 +112,11 @@ namespace py_bindings { // Need __str__ for converting to string py_vector - .def("__str__") + // .def("__str__") // repr allows you to output the object in an interactive python // terminal using obj to get the "string REPResenting the object". - .def("__repr__") - .def(py::self += py::self) + // .def("__repr__") + // .def(py::self += py::self) // Need to do math explicitly converting to DataVector because we don't // want to represent all the possible expression template types .def( diff --git a/backend/src/Utilities/Math/Python/SliceHelpers.hpp b/backend/src/Utilities/Math/Python/SliceHelpers.hpp index a810a9938..cc6a5b5fa 100644 --- a/backend/src/Utilities/Math/Python/SliceHelpers.hpp +++ b/backend/src/Utilities/Math/Python/SliceHelpers.hpp @@ -40,7 +40,7 @@ namespace py_bindings { throw pybind11::error_already_set(); if (step != 1) throw std::runtime_error(std::string( - "step !=1 unsupported along axis " << Axis)); + "step !=1 unsupported along axis ") + std::to_string(Axis)); return SliceInfo{start, slicelength}; } diff --git a/backend/src/meson.build b/backend/src/meson.build index 690dd2722..623d13960 100644 --- a/backend/src/meson.build +++ b/backend/src/meson.build @@ -15,3 +15,17 @@ _linalg = py.extension_module( install: true, subdir: package, ) + +_PyArrays = py.extension_module( + '_PyArrays', + sources: [ + 'Utilities/Math/Python/Bindings.cpp', + 'Utilities/Math/Python/BlazeVector.cpp', + 'Utilities/Math/Python/BlazeMatrix.cpp', + 'Utilities/Math/Python/BlazeTensor.cpp', + ], + dependencies: [py_dep, pybind11_dep, blaze_dep, blaze_tensor_dep], + link_language: 'cpp', + install: true, + subdir: package, +) From 3737d5c969e4ee5b82f362dbac9bf9f036d5a188 Mon Sep 17 00:00:00 2001 From: Ankith Date: Sun, 7 Jul 2024 18:03:50 +0530 Subject: [PATCH 014/121] Add elasticapp documentation --- backend/README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 backend/README.md diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 000000000..60b6c39ec --- /dev/null +++ b/backend/README.md @@ -0,0 +1,33 @@ +# Elasticapp backend for PyElastica + +This file serves as the documentation for the `elasticapp` backend. + +## Installation + +In the root of the PyElastica source tree, run the following command + +``` +pip install ./backend +``` + +This command will take care of installation of all build-time dependencies, compilation of C++ source files and install the a python package called `elasticapp`. + +## Testing + +Make sure you have `pytest` installed. In the root of the PyElastica source tree, run the following command + +``` +pytest backend/tests +``` + +## Benchmarking + +Standalone scripts for benchmarking purposes are available in `backend/benchmarking` folder. + +### Benchmarking `matmul` + +For benchmarking various `matmul` implementations, run + +``` +python3 backend/benchmarking/matmul.py +``` From 4e7a04b85c0270a8219af946fde70a71d1f39258 Mon Sep 17 00:00:00 2001 From: Ankith Date: Sun, 7 Jul 2024 22:24:25 +0530 Subject: [PATCH 015/121] port linalg to use Utils/Math/Python objects - Updated benchmarking and test scripts accordingly --- backend/benchmarking/matmul.py | 40 ++- backend/pyproject.toml | 3 + backend/src/_linalg.cpp | 315 +++--------------------- backend/src/_linalg_numpy.cpp | 376 +++++++++++++++++++++++++++++ backend/src/meson.build | 9 + backend/tests/test_linalg.py | 34 ++- backend/tests/test_linalg_numpy.py | 113 +++++++++ 7 files changed, 581 insertions(+), 309 deletions(-) create mode 100644 backend/src/_linalg_numpy.cpp create mode 100644 backend/tests/test_linalg_numpy.py diff --git a/backend/benchmarking/matmul.py b/backend/benchmarking/matmul.py index 04e4a2b18..87f439767 100644 --- a/backend/benchmarking/matmul.py +++ b/backend/benchmarking/matmul.py @@ -1,4 +1,10 @@ -from elasticapp._linalg import batch_matmul_naive, batch_matmul_blaze, batch_matmul +from elasticapp._linalg_numpy import ( + batch_matmul_naive, + batch_matmul_blaze, + batch_matmul, +) +from elasticapp._linalg import batch_matmul as batch_matmul_final +from elasticapp._PyArrays import Tensor from elastica._linalg import _batch_matmul import numpy import time @@ -16,12 +22,15 @@ def benchmark_batchsize(funcs: list, batches: list[int], num_iterations: int = 1 random_b = numpy.random.random((3, 3, batch_size)) ret[batch_size] = {} - for func in funcs: + for func_name, func, func_wrap in funcs: + random_a_w = func_wrap(random_a) if func_wrap else random_a + random_b_w = func_wrap(random_b) if func_wrap else random_b + start = time.perf_counter() for _ in range(num_iterations): - func(random_a, random_b) + func(random_a_w, random_b_w) - ret[batch_size][func.__name__] = ( + ret[batch_size][func_name] = ( time.perf_counter() - start ) / num_iterations @@ -29,16 +38,25 @@ def benchmark_batchsize(funcs: list, batches: list[int], num_iterations: int = 1 results = benchmark_batchsize( - [batch_matmul_naive, batch_matmul_blaze, batch_matmul, _batch_matmul], [2**i for i in range(14)] + [ + ("pyelastica", _batch_matmul, None), + ("elasticapp_naive", batch_matmul_naive, None), + ("elasticapp_blaze", batch_matmul_blaze, None), + ("elasticapp_blaze_copy", batch_matmul, None), + ("elasticapp_blaze_final", batch_matmul_final, Tensor), + ], + [2**i for i in range(14)], ) for size, data in results.items(): - pyelastica = data["_batch_matmul"] - elasticapp = data["batch_matmul_naive"] - elasticapp_blaze = data["batch_matmul_blaze"] - elasticapp_blaze_v2 = data["batch_matmul"] + pyelastica = data["pyelastica"] + elasticapp_naive = data["elasticapp_naive"] + elasticapp_blaze = data["elasticapp_blaze"] + elasticapp_blaze_copy = data["elasticapp_blaze_copy"] + elasticapp_blaze_final = data["elasticapp_blaze_final"] print(f"{size = }") print(f"{pyelastica = }") - print(f"{elasticapp = }, ratio: {elasticapp / pyelastica}") + print(f"{elasticapp_naive = }, ratio: {elasticapp_naive / pyelastica}") print(f"{elasticapp_blaze = }, ratio: {elasticapp_blaze / pyelastica}") - print(f"{elasticapp_blaze_v2 = }, ratio: {elasticapp_blaze_v2 / pyelastica}") + print(f"{elasticapp_blaze_copy = }, ratio: {elasticapp_blaze_copy / pyelastica}") + print(f"{elasticapp_blaze_final = }, ratio: {elasticapp_blaze_final / pyelastica}") print() diff --git a/backend/pyproject.toml b/backend/pyproject.toml index f10127b1b..35108c608 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -23,6 +23,9 @@ classifiers = [ requires = ["meson-python", "pybind11"] build-backend = "mesonpy" +[tool.meson-python.args] +setup = ['-Doptimization=3'] + # [tool.mypy] # files = "setup.py" # python_version = "3.7" diff --git a/backend/src/_linalg.cpp b/backend/src/_linalg.cpp index 651e29177..f726959d1 100644 --- a/backend/src/_linalg.cpp +++ b/backend/src/_linalg.cpp @@ -10,301 +10,50 @@ using blaze::DynamicMatrix; using blaze::DynamicTensor; using blaze::StaticMatrix; -/* Simple rewrite of elastica._linalg._batch_matmul */ -py::array_t batch_matmul_naive( - py::array_t first_matrix_collection, - py::array_t second_matrix_collection) -{ - auto s1 = first_matrix_collection.shape(0); - auto s2 = first_matrix_collection.shape(1); - auto max_k = first_matrix_collection.shape(2); - auto output_matrix = py::array_t{{s1, s2, max_k}}; - - auto a = first_matrix_collection.unchecked<3>(); - auto b = second_matrix_collection.unchecked<3>(); - auto c = output_matrix.mutable_unchecked<3>(); - for (py::ssize_t i = 0; i < 3; i++) - { - for (py::ssize_t j = 0; j < 3; j++) - { - for (py::ssize_t m = 0; m < 3; m++) - { - for (py::ssize_t k = 0; k < max_k; k++) - { - c(i, m, k) += a(i, j, k) * b(j, m, k); - } - } - } - } - return output_matrix; -} +using ElasticaVector = ::blaze::DynamicVector>; +using ElasticaMatrix = ::blaze::DynamicMatrix>; +using ElasticaTensor = ::blaze::DynamicTensor; -/* blaze implementation of elastica._linalg._batch_matmul */ -py::array_t batch_matmul_blaze( - py::array_t first_matrix_collection, - py::array_t second_matrix_collection) +ElasticaMatrix difference_kernel(ElasticaMatrix &vector_batch) { - auto s1 = first_matrix_collection.shape(0); - auto s2 = first_matrix_collection.shape(1); - auto max_k = first_matrix_collection.shape(2); - auto output_matrix = py::array_t{{s1, s2, max_k}}; - - auto a = first_matrix_collection.unchecked<3>(); - auto b = second_matrix_collection.unchecked<3>(); - auto c = output_matrix.mutable_unchecked<3>(); - - StaticMatrix blaze_a; - StaticMatrix blaze_b; - StaticMatrix blaze_c; - for (py::ssize_t k = 0; k < max_k; k++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - for (py::ssize_t j = 0; j < 3; j++) - { - blaze_a(i, j) = a(i, j, k); - blaze_b(i, j) = b(i, j, k); - } - } - blaze_c = blaze_a * blaze_b; - for (py::ssize_t i = 0; i < 3; i++) - { - for (py::ssize_t j = 0; j < 3; j++) - { - c(i, j, k) = blaze_c(i, j); - } - } - } - - return output_matrix; -} - -py::array_t difference_kernel(py::array_t vector_batch) -{ - const auto v0 = vector_batch.shape(0); - const auto num_elems = vector_batch.shape(1); - - assert(v0 == 3UL); - auto output_arr = py::array_t{{v0, num_elems - 1}}; - - auto vector_batch_unchecked = vector_batch.unchecked<2>(); - auto output_arr_unchecked = output_arr.mutable_unchecked<2>(); - - DynamicMatrix blaze_vector_batch(v0, num_elems); - for (py::ssize_t j = 0; j < num_elems; j++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - blaze_vector_batch(i, j) = vector_batch_unchecked(i, j); - } - } - auto blaze_output = elastica::difference_kernel(blaze_vector_batch); - for (py::ssize_t j = 0; j < num_elems - 1; j++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - output_arr_unchecked(i, j) = blaze_output(i, j); - } - } - - return output_arr; + return elastica::difference_kernel(vector_batch); } -py::array_t batch_matvec( - py::array_t matrix_collection, py::array_t vector_collection) +ElasticaMatrix batch_matvec( + ElasticaTensor &matrix_collection, ElasticaMatrix &vector_collection) { - const auto v0 = vector_collection.shape(0); - const auto num_elems = vector_collection.shape(1); - assert(v0 == 3UL); - - assert(matrix_collection.shape(0) == 3UL); - assert(matrix_collection.shape(1) == 3UL); - assert(matrix_collection.shape(2) == num_elems); - - auto output_arr = py::array_t{{v0, num_elems}}; - - auto matrix_collection_unchecked = matrix_collection.unchecked<3>(); - auto vector_collection_unchecked = vector_collection.unchecked<2>(); - auto output_arr_unchecked = output_arr.mutable_unchecked<2>(); - - DynamicTensor blaze_matrix_collection(v0, v0, num_elems); - DynamicMatrix blaze_vector_collection(v0, num_elems); - for (py::ssize_t k = 0; k < num_elems; k++) - { - for (py::ssize_t j = 0; j < 3; j++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - blaze_matrix_collection(i, j, k) = matrix_collection_unchecked(i, j, k); - } - blaze_vector_collection(j, k) = vector_collection_unchecked(j, k); - } - } - - DynamicMatrix blaze_output(v0, num_elems); - elastica::batch_matvec(blaze_output, blaze_matrix_collection, blaze_vector_collection); - for (py::ssize_t j = 0; j < num_elems; j++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - output_arr_unchecked(i, j) = blaze_output(i, j); - } - } - - return output_arr; + ElasticaMatrix blaze_output(matrix_collection.rows(), matrix_collection.columns()); + elastica::batch_matvec(blaze_output, matrix_collection, vector_collection); + return blaze_output; } -py::array_t batch_matmul( - py::array_t first_matrix_batch, py::array_t second_matrix_batch) +ElasticaTensor batch_matmul( + ElasticaTensor &first_matrix_batch, ElasticaTensor &second_matrix_batch) { - const auto m0 = first_matrix_batch.shape(0); - const auto m1 = first_matrix_batch.shape(1); - const auto num_elems = first_matrix_batch.shape(2); - assert(m0 == 3UL); - assert(m1 == 3UL); - - assert(second_matrix_batch.shape(0) == 3UL); - assert(second_matrix_batch.shape(1) == 3UL); - assert(second_matrix_batch.shape(2) == num_elems); - - auto output_arr = py::array_t{{m0, m1, num_elems}}; - - auto first_matrix_batch_unchecked = first_matrix_batch.unchecked<3>(); - auto second_matrix_batch_unchecked = second_matrix_batch.unchecked<3>(); - auto output_arr_unchecked = output_arr.mutable_unchecked<3>(); - - DynamicTensor blaze_first_matrix_batch(m0, m1, num_elems); - DynamicTensor blaze_second_matrix_batch(m0, m1, num_elems); - for (py::ssize_t k = 0; k < num_elems; k++) - { - for (py::ssize_t j = 0; j < 3; j++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - blaze_first_matrix_batch(i, j, k) = first_matrix_batch_unchecked(i, j, k); - blaze_second_matrix_batch(i, j, k) = second_matrix_batch_unchecked(i, j, k); - } - } - } - DynamicTensor blaze_output(m0, m1, num_elems); - elastica::batch_matmul(blaze_output, blaze_first_matrix_batch, blaze_second_matrix_batch); - for (py::ssize_t k = 0; k < num_elems; k++) - { - for (py::ssize_t j = 0; j < 3; j++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - output_arr_unchecked(i, j, k) = blaze_output(i, j, k); - } - } - } - return output_arr; + ElasticaTensor blaze_output(first_matrix_batch.pages(), first_matrix_batch.rows(), first_matrix_batch.columns()); + elastica::batch_matmul(blaze_output, first_matrix_batch, second_matrix_batch); + return blaze_output; } -py::array_t batch_cross( - py::array_t first_vector_batch, py::array_t second_vector_batch) +ElasticaMatrix batch_cross( + ElasticaMatrix &first_vector_batch, ElasticaMatrix &second_vector_batch) { - const auto v0 = first_vector_batch.shape(0); - const auto num_elems = first_vector_batch.shape(1); - assert(v0 == 3UL); - - assert(second_vector_batch.shape(0) == 3UL); - assert(second_vector_batch.shape(1) == num_elems); - - auto output_arr = py::array_t{{v0, num_elems}}; - - auto first_vector_batch_unchecked = first_vector_batch.unchecked<2>(); - auto second_vector_batch_unchecked = second_vector_batch.unchecked<2>(); - auto output_arr_unchecked = output_arr.mutable_unchecked<2>(); - - DynamicMatrix blaze_first_vector_batch(v0, num_elems); - DynamicMatrix blaze_second_vector_batch(v0, num_elems); - for (py::ssize_t j = 0; j < num_elems; j++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - blaze_first_vector_batch(i, j) = first_vector_batch_unchecked(i, j); - blaze_second_vector_batch(i, j) = second_vector_batch_unchecked(i, j); - } - } - - DynamicMatrix blaze_output(v0, num_elems); - elastica::batch_cross(blaze_output, blaze_first_vector_batch, blaze_second_vector_batch); - for (py::ssize_t j = 0; j < num_elems; j++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - output_arr_unchecked(i, j) = blaze_output(i, j); - } - } - - return output_arr; + ElasticaMatrix blaze_output(first_vector_batch.rows(), first_vector_batch.columns()); + elastica::batch_cross(blaze_output, first_vector_batch, second_vector_batch); + return blaze_output; } -py::array_t batch_dot( - py::array_t first_vector_batch, py::array_t second_vector_batch) +ElasticaVector batch_dot( + ElasticaMatrix &first_vector_batch, ElasticaMatrix &second_vector_batch) { - const auto v0 = first_vector_batch.shape(0); - const auto num_elems = first_vector_batch.shape(1); - assert(v0 == 3UL); - - assert(second_vector_batch.shape(0) == 3UL); - assert(second_vector_batch.shape(1) == num_elems); - - auto output_arr = py::array_t{num_elems}; - - auto first_vector_batch_unchecked = first_vector_batch.unchecked<2>(); - auto second_vector_batch_unchecked = second_vector_batch.unchecked<2>(); - auto output_arr_unchecked = output_arr.mutable_unchecked<1>(); - - DynamicMatrix blaze_first_vector_batch(v0, num_elems); - DynamicMatrix blaze_second_vector_batch(v0, num_elems); - for (py::ssize_t j = 0; j < num_elems; j++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - blaze_first_vector_batch(i, j) = first_vector_batch_unchecked(i, j); - blaze_second_vector_batch(i, j) = second_vector_batch_unchecked(i, j); - } - } - - auto blaze_output = elastica::batch_dot(blaze_first_vector_batch, blaze_second_vector_batch); - for (py::ssize_t j = 0; j < num_elems; j++) - { - output_arr_unchecked(j) = blaze_output[j]; - } - - return output_arr; + return elastica::batch_dot(first_vector_batch, second_vector_batch); } -py::array_t batch_norm(py::array_t vector_batch) +ElasticaVector batch_norm(ElasticaMatrix &vector_batch) { - const auto v0 = vector_batch.shape(0); - const auto num_elems = vector_batch.shape(1); - - assert(v0 == 3UL); - - auto output_arr = py::array_t{num_elems}; - - auto vector_batch_unchecked = vector_batch.unchecked<2>(); - auto output_arr_unchecked = output_arr.mutable_unchecked<1>(); - - DynamicMatrix blaze_vector_batch(v0, num_elems); - for (py::ssize_t j = 0; j < num_elems; j++) - { - for (py::ssize_t i = 0; i < 3; i++) - { - blaze_vector_batch(i, j) = vector_batch_unchecked(i, j); - } - } - - auto blaze_output = elastica::batch_norm(blaze_vector_batch); - for (py::ssize_t j = 0; j < num_elems; j++) - { - output_arr_unchecked(j) = blaze_output[j]; - } - - return output_arr; + return elastica::batch_norm(vector_batch); } PYBIND11_MODULE(_linalg, m) @@ -328,16 +77,6 @@ PYBIND11_MODULE(_linalg, m) batch_norm )pbdoc"; - m.def("batch_matmul_naive", &batch_matmul_naive, R"pbdoc( - This is batch matrix matrix multiplication function. Only batch - of 3x3 matrices can be multiplied. - )pbdoc"); - - m.def("batch_matmul_blaze", &batch_matmul_blaze, R"pbdoc( - This is batch matrix matrix multiplication function. Only batch - of 3x3 matrices can be multiplied. - )pbdoc"); - m.def("difference_kernel", &difference_kernel, R"pbdoc( Vector Difference )pbdoc"); diff --git a/backend/src/_linalg_numpy.cpp b/backend/src/_linalg_numpy.cpp new file mode 100644 index 000000000..061d3df07 --- /dev/null +++ b/backend/src/_linalg_numpy.cpp @@ -0,0 +1,376 @@ +#include +#include +#include + +#include "Utilities/Math/BlazeDetail/BlazeLinearAlgebra.hpp" + +namespace py = pybind11; + +using blaze::DynamicMatrix; +using blaze::DynamicTensor; +using blaze::StaticMatrix; + +/* Simple rewrite of elastica._linalg._batch_matmul */ +py::array_t batch_matmul_naive( + py::array_t first_matrix_collection, + py::array_t second_matrix_collection) +{ + auto s1 = first_matrix_collection.shape(0); + auto s2 = first_matrix_collection.shape(1); + auto max_k = first_matrix_collection.shape(2); + auto output_matrix = py::array_t{{s1, s2, max_k}}; + + auto a = first_matrix_collection.unchecked<3>(); + auto b = second_matrix_collection.unchecked<3>(); + auto c = output_matrix.mutable_unchecked<3>(); + for (py::ssize_t i = 0; i < 3; i++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + for (py::ssize_t m = 0; m < 3; m++) + { + for (py::ssize_t k = 0; k < max_k; k++) + { + c(i, m, k) += a(i, j, k) * b(j, m, k); + } + } + } + } + return output_matrix; +} + +/* blaze implementation of elastica._linalg._batch_matmul */ +py::array_t batch_matmul_blaze( + py::array_t first_matrix_collection, + py::array_t second_matrix_collection) +{ + auto s1 = first_matrix_collection.shape(0); + auto s2 = first_matrix_collection.shape(1); + auto max_k = first_matrix_collection.shape(2); + auto output_matrix = py::array_t{{s1, s2, max_k}}; + + auto a = first_matrix_collection.unchecked<3>(); + auto b = second_matrix_collection.unchecked<3>(); + auto c = output_matrix.mutable_unchecked<3>(); + + StaticMatrix blaze_a; + StaticMatrix blaze_b; + StaticMatrix blaze_c; + for (py::ssize_t k = 0; k < max_k; k++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + blaze_a(i, j) = a(i, j, k); + blaze_b(i, j) = b(i, j, k); + } + } + blaze_c = blaze_a * blaze_b; + for (py::ssize_t i = 0; i < 3; i++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + c(i, j, k) = blaze_c(i, j); + } + } + } + + return output_matrix; +} + +py::array_t difference_kernel(py::array_t vector_batch) +{ + const auto v0 = vector_batch.shape(0); + const auto num_elems = vector_batch.shape(1); + + assert(v0 == 3UL); + auto output_arr = py::array_t{{v0, num_elems - 1}}; + + auto vector_batch_unchecked = vector_batch.unchecked<2>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<2>(); + + DynamicMatrix blaze_vector_batch(v0, num_elems); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_vector_batch(i, j) = vector_batch_unchecked(i, j); + } + } + auto blaze_output = elastica::difference_kernel(blaze_vector_batch); + for (py::ssize_t j = 0; j < num_elems - 1; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + output_arr_unchecked(i, j) = blaze_output(i, j); + } + } + + return output_arr; +} + +py::array_t batch_matvec( + py::array_t matrix_collection, py::array_t vector_collection) +{ + const auto v0 = vector_collection.shape(0); + const auto num_elems = vector_collection.shape(1); + assert(v0 == 3UL); + + assert(matrix_collection.shape(0) == 3UL); + assert(matrix_collection.shape(1) == 3UL); + assert(matrix_collection.shape(2) == num_elems); + + auto output_arr = py::array_t{{v0, num_elems}}; + + auto matrix_collection_unchecked = matrix_collection.unchecked<3>(); + auto vector_collection_unchecked = vector_collection.unchecked<2>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<2>(); + + DynamicTensor blaze_matrix_collection(v0, v0, num_elems); + DynamicMatrix blaze_vector_collection(v0, num_elems); + for (py::ssize_t k = 0; k < num_elems; k++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_matrix_collection(i, j, k) = matrix_collection_unchecked(i, j, k); + } + blaze_vector_collection(j, k) = vector_collection_unchecked(j, k); + } + } + + DynamicMatrix blaze_output(v0, num_elems); + elastica::batch_matvec(blaze_output, blaze_matrix_collection, blaze_vector_collection); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + output_arr_unchecked(i, j) = blaze_output(i, j); + } + } + + return output_arr; +} + +py::array_t batch_matmul( + py::array_t first_matrix_batch, py::array_t second_matrix_batch) +{ + const auto m0 = first_matrix_batch.shape(0); + const auto m1 = first_matrix_batch.shape(1); + const auto num_elems = first_matrix_batch.shape(2); + assert(m0 == 3UL); + assert(m1 == 3UL); + + assert(second_matrix_batch.shape(0) == 3UL); + assert(second_matrix_batch.shape(1) == 3UL); + assert(second_matrix_batch.shape(2) == num_elems); + + auto output_arr = py::array_t{{m0, m1, num_elems}}; + + auto first_matrix_batch_unchecked = first_matrix_batch.unchecked<3>(); + auto second_matrix_batch_unchecked = second_matrix_batch.unchecked<3>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<3>(); + + DynamicTensor blaze_first_matrix_batch(m0, m1, num_elems); + DynamicTensor blaze_second_matrix_batch(m0, m1, num_elems); + for (py::ssize_t k = 0; k < num_elems; k++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_first_matrix_batch(i, j, k) = first_matrix_batch_unchecked(i, j, k); + blaze_second_matrix_batch(i, j, k) = second_matrix_batch_unchecked(i, j, k); + } + } + } + DynamicTensor blaze_output(m0, m1, num_elems); + elastica::batch_matmul(blaze_output, blaze_first_matrix_batch, blaze_second_matrix_batch); + for (py::ssize_t k = 0; k < num_elems; k++) + { + for (py::ssize_t j = 0; j < 3; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + output_arr_unchecked(i, j, k) = blaze_output(i, j, k); + } + } + } + return output_arr; +} + +py::array_t batch_cross( + py::array_t first_vector_batch, py::array_t second_vector_batch) +{ + const auto v0 = first_vector_batch.shape(0); + const auto num_elems = first_vector_batch.shape(1); + assert(v0 == 3UL); + + assert(second_vector_batch.shape(0) == 3UL); + assert(second_vector_batch.shape(1) == num_elems); + + auto output_arr = py::array_t{{v0, num_elems}}; + + auto first_vector_batch_unchecked = first_vector_batch.unchecked<2>(); + auto second_vector_batch_unchecked = second_vector_batch.unchecked<2>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<2>(); + + DynamicMatrix blaze_first_vector_batch(v0, num_elems); + DynamicMatrix blaze_second_vector_batch(v0, num_elems); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_first_vector_batch(i, j) = first_vector_batch_unchecked(i, j); + blaze_second_vector_batch(i, j) = second_vector_batch_unchecked(i, j); + } + } + + DynamicMatrix blaze_output(v0, num_elems); + elastica::batch_cross(blaze_output, blaze_first_vector_batch, blaze_second_vector_batch); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + output_arr_unchecked(i, j) = blaze_output(i, j); + } + } + + return output_arr; +} + +py::array_t batch_dot( + py::array_t first_vector_batch, py::array_t second_vector_batch) +{ + const auto v0 = first_vector_batch.shape(0); + const auto num_elems = first_vector_batch.shape(1); + assert(v0 == 3UL); + + assert(second_vector_batch.shape(0) == 3UL); + assert(second_vector_batch.shape(1) == num_elems); + + auto output_arr = py::array_t{num_elems}; + + auto first_vector_batch_unchecked = first_vector_batch.unchecked<2>(); + auto second_vector_batch_unchecked = second_vector_batch.unchecked<2>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<1>(); + + DynamicMatrix blaze_first_vector_batch(v0, num_elems); + DynamicMatrix blaze_second_vector_batch(v0, num_elems); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_first_vector_batch(i, j) = first_vector_batch_unchecked(i, j); + blaze_second_vector_batch(i, j) = second_vector_batch_unchecked(i, j); + } + } + + auto blaze_output = elastica::batch_dot(blaze_first_vector_batch, blaze_second_vector_batch); + for (py::ssize_t j = 0; j < num_elems; j++) + { + output_arr_unchecked(j) = blaze_output[j]; + } + + return output_arr; +} + +py::array_t batch_norm(py::array_t vector_batch) +{ + const auto v0 = vector_batch.shape(0); + const auto num_elems = vector_batch.shape(1); + + assert(v0 == 3UL); + + auto output_arr = py::array_t{num_elems}; + + auto vector_batch_unchecked = vector_batch.unchecked<2>(); + auto output_arr_unchecked = output_arr.mutable_unchecked<1>(); + + DynamicMatrix blaze_vector_batch(v0, num_elems); + for (py::ssize_t j = 0; j < num_elems; j++) + { + for (py::ssize_t i = 0; i < 3; i++) + { + blaze_vector_batch(i, j) = vector_batch_unchecked(i, j); + } + } + + auto blaze_output = elastica::batch_norm(blaze_vector_batch); + for (py::ssize_t j = 0; j < num_elems; j++) + { + output_arr_unchecked(j) = blaze_output[j]; + } + + return output_arr; +} + +PYBIND11_MODULE(_linalg_numpy, m) +{ + m.doc() = R"pbdoc( + elasticapp _linalg_numpy + --------------- + + .. currentmodule:: _linalg_numpy + + .. autosummary:: + :toctree: _generate + + batch_matmul_naive + batch_matmul_blaze + difference_kernel + batch_matvec + batch_matmul + batch_cross + batch_dot + batch_norm + )pbdoc"; + + m.def("batch_matmul_naive", &batch_matmul_naive, R"pbdoc( + This is batch matrix matrix multiplication function. Only batch + of 3x3 matrices can be multiplied. + )pbdoc"); + + m.def("batch_matmul_blaze", &batch_matmul_blaze, R"pbdoc( + This is batch matrix matrix multiplication function. Only batch + of 3x3 matrices can be multiplied. + )pbdoc"); + + m.def("difference_kernel", &difference_kernel, R"pbdoc( + Vector Difference + )pbdoc"); + + m.def("batch_matvec", &batch_matvec, R"pbdoc( + Computes a batchwise matrix-vector product given in indical notation: + matvec_batch{ik} = matrix_batch{ijk} * vector_batch{jk} + )pbdoc"); + + m.def("batch_matmul", &batch_matmul, R"pbdoc( + Computes a batchwise matrix-matrix product given in indical notation: + matmul_batch{ilk} = first_matrix_batch{ijk} * second_matrix_batch{jlk} + )pbdoc"); + + m.def("batch_cross", &batch_cross, R"pbdoc( + Computes a batchwise vector-vector cross product given in indical notation: + cross_batch{il} = LCT{ijk} * first_vector_batch{jl} * second_vector_batch{kl} + where LCT is the Levi-Civita Tensor + )pbdoc"); + + m.def("batch_dot", &batch_dot, R"pbdoc( + Computes a batchwise vector-vector dot product given in indical notation: + dot_batch{j} = first_vector_batch{ij} * second_vector_batch{ij} + )pbdoc"); + + m.def("batch_norm", &batch_norm, R"pbdoc( + Computes a batchwise vector L2 norm given in indical notation: + norm_batch{j} = (vector_batch{ij} * vector_batch{ij})^0.5 + )pbdoc"); + +#ifdef VERSION_INFO + m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); +#else + m.attr("__version__") = "dev"; +#endif +} diff --git a/backend/src/meson.build b/backend/src/meson.build index 623d13960..42b030ee4 100644 --- a/backend/src/meson.build +++ b/backend/src/meson.build @@ -16,6 +16,15 @@ _linalg = py.extension_module( subdir: package, ) +_linalg_numpy = py.extension_module( + '_linalg_numpy', + sources: ['_linalg_numpy.cpp'], + dependencies: [py_dep, pybind11_dep, blaze_dep, blaze_tensor_dep], + link_language: 'cpp', + install: true, + subdir: package, +) + _PyArrays = py.extension_module( '_PyArrays', sources: [ diff --git a/backend/tests/test_linalg.py b/backend/tests/test_linalg.py index 1fba0332b..da92cb370 100644 --- a/backend/tests/test_linalg.py +++ b/backend/tests/test_linalg.py @@ -6,6 +6,7 @@ import numpy as np import pytest from numpy.testing import assert_allclose +from elasticapp._PyArrays import Matrix, Tensor from elasticapp._linalg import ( difference_kernel, batch_matvec, @@ -19,7 +20,9 @@ @pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) def test_difference_kernel(blocksize: int): input_vector_collection = np.random.randn(3, blocksize) - output_vector_collection = difference_kernel(input_vector_collection) + output_vector_collection = np.asarray( + difference_kernel(Matrix(input_vector_collection)) + ) correct_vector_collection = ( input_vector_collection[:, 1:] - input_vector_collection[:, :-1] @@ -33,8 +36,8 @@ def test_batch_matvec(blocksize: int): input_matrix_collection = np.random.randn(3, 3, blocksize) input_vector_collection = np.random.randn(3, blocksize) - test_vector_collection = batch_matvec( - input_matrix_collection, input_vector_collection + test_vector_collection = np.asarray( + batch_matvec(Tensor(input_matrix_collection), Matrix(input_vector_collection)) ) correct_vector_collection = [ @@ -51,8 +54,11 @@ def test_batch_matmul(blocksize: int): input_first_matrix_collection = np.random.randn(3, 3, blocksize) input_second_matrix_collection = np.random.randn(3, 3, blocksize) - test_matrix_collection = batch_matmul( - input_first_matrix_collection, input_second_matrix_collection + test_matrix_collection = np.asarray( + batch_matmul( + Tensor(input_first_matrix_collection), + Tensor(input_second_matrix_collection), + ) ) correct_matrix_collection = np.empty((3, 3, blocksize)) @@ -72,8 +78,11 @@ def test_batch_cross(dim, blocksize: int): input_first_vector_collection = np.random.randn(dim, blocksize) input_second_vector_collection = np.random.randn(dim, blocksize) - test_vector_collection = batch_cross( - input_first_vector_collection, input_second_vector_collection + test_vector_collection = np.asarray( + batch_cross( + Matrix(input_first_vector_collection), + Matrix(input_second_vector_collection), + ) ) correct_vector_collection = np.cross( input_first_vector_collection, input_second_vector_collection, axisa=0, axisb=0 @@ -87,8 +96,11 @@ def test_batch_dot(blocksize: int): input_first_vector_collection = np.random.randn(3, blocksize) input_second_vector_collection = np.random.randn(3, blocksize) - test_vector_collection = batch_dot( - input_first_vector_collection, input_second_vector_collection + test_vector_collection = np.asarray( + batch_dot( + Matrix(input_first_vector_collection), + Matrix(input_second_vector_collection), + ) ) correct_vector_collection = np.einsum( @@ -102,7 +114,9 @@ def test_batch_dot(blocksize: int): def test_batch_norm(blocksize: int): input_first_vector_collection = np.random.randn(3, blocksize) - test_vector_collection = batch_norm(input_first_vector_collection) + test_vector_collection = np.asarray( + batch_norm(Matrix(input_first_vector_collection)) + ) correct_vector_collection = np.sqrt( np.einsum( diff --git a/backend/tests/test_linalg_numpy.py b/backend/tests/test_linalg_numpy.py new file mode 100644 index 000000000..fe4dc0abc --- /dev/null +++ b/backend/tests/test_linalg_numpy.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 + +# This file is based on pyelastica tests/test_math/test_linalg.py + +# System imports +import numpy as np +import pytest +from numpy.testing import assert_allclose +from elasticapp._linalg_numpy import ( + difference_kernel, + batch_matvec, + batch_matmul, + batch_cross, + batch_dot, + batch_norm, +) + + +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_difference_kernel(blocksize: int): + input_vector_collection = np.random.randn(3, blocksize) + output_vector_collection = difference_kernel(input_vector_collection) + + correct_vector_collection = ( + input_vector_collection[:, 1:] - input_vector_collection[:, :-1] + ) + + assert_allclose(output_vector_collection, correct_vector_collection) + + +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_batch_matvec(blocksize: int): + input_matrix_collection = np.random.randn(3, 3, blocksize) + input_vector_collection = np.random.randn(3, blocksize) + + test_vector_collection = batch_matvec( + input_matrix_collection, input_vector_collection + ) + + correct_vector_collection = [ + np.dot(input_matrix_collection[..., i], input_vector_collection[..., i]) + for i in range(blocksize) + ] + correct_vector_collection = np.array(correct_vector_collection).T + + assert_allclose(test_vector_collection, correct_vector_collection) + + +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_batch_matmul(blocksize: int): + input_first_matrix_collection = np.random.randn(3, 3, blocksize) + input_second_matrix_collection = np.random.randn(3, 3, blocksize) + + test_matrix_collection = batch_matmul( + input_first_matrix_collection, input_second_matrix_collection + ) + + correct_matrix_collection = np.empty((3, 3, blocksize)) + for i in range(blocksize): + correct_matrix_collection[..., i] = np.dot( + input_first_matrix_collection[..., i], + input_second_matrix_collection[..., i], + ) + + assert_allclose(test_matrix_collection, correct_matrix_collection) + + +# TODO : Generalize to two dimensions +@pytest.mark.parametrize("dim", [3]) +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_batch_cross(dim, blocksize: int): + input_first_vector_collection = np.random.randn(dim, blocksize) + input_second_vector_collection = np.random.randn(dim, blocksize) + + test_vector_collection = batch_cross( + input_first_vector_collection, input_second_vector_collection + ) + correct_vector_collection = np.cross( + input_first_vector_collection, input_second_vector_collection, axisa=0, axisb=0 + ).T + + assert_allclose(test_vector_collection, correct_vector_collection) + + +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_batch_dot(blocksize: int): + input_first_vector_collection = np.random.randn(3, blocksize) + input_second_vector_collection = np.random.randn(3, blocksize) + + test_vector_collection = batch_dot( + input_first_vector_collection, input_second_vector_collection + ) + + correct_vector_collection = np.einsum( + "ij,ij->j", input_first_vector_collection, input_second_vector_collection + ) + + assert_allclose(test_vector_collection, correct_vector_collection) + + +@pytest.mark.parametrize("blocksize", [1, 2, 8, 32]) +def test_batch_norm(blocksize: int): + input_first_vector_collection = np.random.randn(3, blocksize) + + test_vector_collection = batch_norm(input_first_vector_collection) + + correct_vector_collection = np.sqrt( + np.einsum( + "ij,ij->j", input_first_vector_collection, input_first_vector_collection + ) + ) + + assert_allclose(test_vector_collection, correct_vector_collection) From f9d4d001fd6a715bbe371803b629a1d88428a176 Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Tue, 9 Jul 2024 22:29:12 -0500 Subject: [PATCH 016/121] Update README.md --- backend/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/README.md b/backend/README.md index 60b6c39ec..33bbee46f 100644 --- a/backend/README.md +++ b/backend/README.md @@ -10,6 +10,8 @@ In the root of the PyElastica source tree, run the following command pip install ./backend ``` +> Make sure you install the package from _PyElastica source tree_. + This command will take care of installation of all build-time dependencies, compilation of C++ source files and install the a python package called `elasticapp`. ## Testing From 7d45e74747344d91009b3d7bb525296d8ea6f3b3 Mon Sep 17 00:00:00 2001 From: Ankith Date: Wed, 10 Jul 2024 17:45:20 +0530 Subject: [PATCH 017/121] Add batchcross benchmarking --- backend/benchmarking/batchcross.py | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 backend/benchmarking/batchcross.py diff --git a/backend/benchmarking/batchcross.py b/backend/benchmarking/batchcross.py new file mode 100644 index 000000000..eb9cc78b0 --- /dev/null +++ b/backend/benchmarking/batchcross.py @@ -0,0 +1,50 @@ +from elasticapp._linalg_numpy import batch_cross +from elasticapp._linalg import batch_cross as batch_cross_final +from elasticapp._PyArrays import Matrix +from elastica._linalg import _batch_cross +import numpy +import time + +# warm up jit for fair comparison +random_1 = numpy.random.random((3, 1)) +random_2 = numpy.random.random((3, 1)) +out1 = _batch_cross(random_1, random_2) + + +def benchmark_batchsize(funcs: list, batches: list[int], num_iterations: int = 1000): + ret: dict = {} + for batch_size in batches: + random_a = numpy.random.random((3, batch_size)) + random_b = numpy.random.random((3, batch_size)) + + ret[batch_size] = {} + for func_name, func, func_wrap in funcs: + random_a_w = func_wrap(random_a) if func_wrap else random_a + random_b_w = func_wrap(random_b) if func_wrap else random_b + + start = time.perf_counter() + for _ in range(num_iterations): + func(random_a_w, random_b_w) + + ret[batch_size][func_name] = (time.perf_counter() - start) / num_iterations + + return ret + + +results = benchmark_batchsize( + [ + ("pyelastica", _batch_cross, None), + ("elasticapp_blaze_copy", batch_cross, None), + ("elasticapp_blaze_final", batch_cross_final, Matrix), + ], + [2**i for i in range(14)], +) +for size, data in results.items(): + pyelastica = data["pyelastica"] + elasticapp_blaze_copy = data["elasticapp_blaze_copy"] + elasticapp_blaze_final = data["elasticapp_blaze_final"] + print(f"{size = }") + print(f"{pyelastica = }") + print(f"{elasticapp_blaze_copy = }, ratio: {elasticapp_blaze_copy / pyelastica}") + print(f"{elasticapp_blaze_final = }, ratio: {elasticapp_blaze_final / pyelastica}") + print() From f1eaef8a8da7aa1299b271147017acfecd2b7f0e Mon Sep 17 00:00:00 2001 From: Ankith Date: Wed, 10 Jul 2024 17:48:50 +0530 Subject: [PATCH 018/121] Setup optimization blaze defines globally --- backend/meson.build | 37 +++++++++++++++++++++++++++++++++++++ backend/src/meson.build | 6 +++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/backend/meson.build b/backend/meson.build index 4c5cce8f2..dc97db5f4 100644 --- a/backend/meson.build +++ b/backend/meson.build @@ -12,6 +12,40 @@ cc = meson.get_compiler('cpp') py = import('python').find_installation(pure: false) py_dep = py.dependency() +# set up global defines to optimize blaze usages +add_global_arguments( + # - Enable external BLAS kernels + '-DBLAZE_BLAS_MODE=0', + # - Set default matrix storage order to column-major, since many of our + # functions are implemented for column-major layout. This default reduces + # conversions. + '-DBLAZE_DEFAULT_STORAGE_ORDER=blaze::rowMajor', + # - Disable SMP parallelization. This disables SMP parallelization for all + # possible backends (OpenMP, C++11 threads, Boost, HPX): + # https://bitbucket.org/blaze-lib/blaze/wiki/Serial%20Execution#!option-3-deactivation-of-parallel-execution + '-DBLAZE_USE_SHARED_MEMORY_PARALLELIZATION=0', + # - Disable MPI parallelization + '-DBLAZE_MPI_PARALLEL_MODE=0', + # - Using the default cache size, which may have been configured automatically + # by the Blaze CMake configuration for the machine we are running on. We + # could override it here explicitly to tune performance. + # BLAZE_CACHE_SIZE + '-DBLAZE_USE_PADDING=1', + # - Always enable non-temporal stores for cache optimization of large data + # structures: https://bitbucket.org/blaze-lib/blaze/wiki/Configuration%20Files#!streaming-non-temporal-stores + '-DBLAZE_USE_STREAMING=1', + # - Initializing default-constructed structures for fundamental types + '-DBLAZE_USE_DEFAULT_INITIALIZATON=1', + # Use Sleef for vectorization of more math functions + '-DBLAZE_USE_SLEEF=1', + # Set inlining settings + '-DBLAZE_USE_STRONG_INLINE=1', + '-DBLAZE_USE_ALWAYS_INLINE=1', + # Set vectorization (leave to 1, else there is no use for blaze) + '-DBLAZE_USE_VECTORIZATION=1', + language: 'cpp' +) + # find dependencies and create dep objects pybind11_dep = declare_dependency( include_directories: run_command( @@ -53,6 +87,9 @@ if not sleef_dep.found() ) endif +# blaze and deps commonly used together +blaze_deps = [blaze_dep, blaze_tensor_dep, sleef_dep] + brigand_dep = dependency('brigand', fallback : 'brigand') cxxopts_dep = dependency('cxxopts', fallback : 'cxxopts') diff --git a/backend/src/meson.build b/backend/src/meson.build index 42b030ee4..fcbda76b8 100644 --- a/backend/src/meson.build +++ b/backend/src/meson.build @@ -10,7 +10,7 @@ example_1 = py.extension_module( _linalg = py.extension_module( '_linalg', sources: ['_linalg.cpp'], - dependencies: [py_dep, pybind11_dep, blaze_dep, blaze_tensor_dep], + dependencies: [py_dep, pybind11_dep] + blaze_deps, link_language: 'cpp', install: true, subdir: package, @@ -19,7 +19,7 @@ _linalg = py.extension_module( _linalg_numpy = py.extension_module( '_linalg_numpy', sources: ['_linalg_numpy.cpp'], - dependencies: [py_dep, pybind11_dep, blaze_dep, blaze_tensor_dep], + dependencies: [py_dep, pybind11_dep] + blaze_deps, link_language: 'cpp', install: true, subdir: package, @@ -33,7 +33,7 @@ _PyArrays = py.extension_module( 'Utilities/Math/Python/BlazeMatrix.cpp', 'Utilities/Math/Python/BlazeTensor.cpp', ], - dependencies: [py_dep, pybind11_dep, blaze_dep, blaze_tensor_dep], + dependencies: [py_dep, pybind11_dep] + blaze_deps, link_language: 'cpp', install: true, subdir: package, From a61a4450cde064774bdf169ccfa1393fbd898597 Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Wed, 10 Jul 2024 21:49:04 -0500 Subject: [PATCH 019/121] revert: __str__ for blaze objects. Other print-related methods are added. --- .../src/Utilities/Math/Python/BlazeMatrix.cpp | 14 ++-- .../src/Utilities/Math/Python/BlazeTensor.cpp | 12 +-- .../src/Utilities/Math/Python/BlazeVector.cpp | 20 +++-- backend/src/Utilities/PrintHelpers.hpp | 72 ++++++++++++++++++ backend/src/Utilities/Requires.hpp | 70 +++++++++++++++++ .../src/Utilities/StlStreamDeclarations.hpp | 76 +++++++++++++++++++ 6 files changed, 246 insertions(+), 18 deletions(-) create mode 100644 backend/src/Utilities/PrintHelpers.hpp create mode 100644 backend/src/Utilities/Requires.hpp create mode 100644 backend/src/Utilities/StlStreamDeclarations.hpp diff --git a/backend/src/Utilities/Math/Python/BlazeMatrix.cpp b/backend/src/Utilities/Math/Python/BlazeMatrix.cpp index 56f55a110..4ebe13deb 100644 --- a/backend/src/Utilities/Math/Python/BlazeMatrix.cpp +++ b/backend/src/Utilities/Math/Python/BlazeMatrix.cpp @@ -6,7 +6,7 @@ // #include "PythonBindings/BoundChecks.hpp" // #include "Utilities/DefineTypes.h" -// #include "Utilities/MakeString.hpp" +#include "Utilities/MakeString.hpp" // #include "Utilities/Math/Python/SliceHelpers.hpp" // @@ -81,7 +81,7 @@ namespace py_bindings { "__getitem__", +[](const type& self, const std::tuple& x) { - // matrix_bounds_check(self, std::get<0>(x), std::get<1>(x)); + matrix_bounds_check(self, std::get<0>(x), std::get<1>(x)); return self(std::get<0>(x), std::get<1>(x)); }) .def( @@ -90,16 +90,16 @@ namespace py_bindings { return array_slice(t, std::move(slice)); }) // Need __str__ for converting to string/printing - // .def( - // "__str__", - // +[](const type& self) { return std::string(MakeString{} << self); }) + .def( + "__str__", + +[](const type& self) { return std::string(MakeString{} << self); }) .def( "__setitem__", +[](type& self, const std::tuple& x, const Real val) { - // matrix_bounds_check(self, std::get<0>(x), std::get<1>(x)); + matrix_bounds_check(self, std::get<0>(x), std::get<1>(x)); self(std::get<0>(x), std::get<1>(x)) = val; - }); + }); } //**************************************************************************** diff --git a/backend/src/Utilities/Math/Python/BlazeTensor.cpp b/backend/src/Utilities/Math/Python/BlazeTensor.cpp index 156f7f891..9ed4d01c4 100644 --- a/backend/src/Utilities/Math/Python/BlazeTensor.cpp +++ b/backend/src/Utilities/Math/Python/BlazeTensor.cpp @@ -6,7 +6,7 @@ // #include "PythonBindings/BoundChecks.hpp" // #include "Utilities/DefineTypes.h" -// #include "Utilities/MakeString.hpp" +#include "Utilities/MakeString.hpp" // #include "Utilities/Math/Python/SliceHelpers.hpp" // @@ -99,12 +99,12 @@ namespace py_bindings { // tensor_bounds_check(self, std::get<0>(x), std::get<1>(x), // std::get<2>(x)); self(std::get<0>(x), std::get<1>(x), std::get<2>(x)) = val; - }); + }) // Need __str__ for converting to string/printing - // .def( - // "__str__", +[](const type& self) { - // return std::string(MakeString{} << self); - // }); + .def( + "__str__", +[](const type& self) { + return std::string(MakeString{} << self); + }); } //**************************************************************************** diff --git a/backend/src/Utilities/Math/Python/BlazeVector.cpp b/backend/src/Utilities/Math/Python/BlazeVector.cpp index 86239102f..eed3afa66 100644 --- a/backend/src/Utilities/Math/Python/BlazeVector.cpp +++ b/backend/src/Utilities/Math/Python/BlazeVector.cpp @@ -5,6 +5,8 @@ // #include "PythonBindings/BoundChecks.hpp" // #include "Utilities/DefineTypes.h" +// #include "Utilities/PrettyType.hpp" +#include "Utilities/PrintHelpers.hpp" // #include "Utilities/Math/Python/SliceHelpers.hpp" // @@ -96,7 +98,7 @@ namespace py_bindings { .def( "__getitem__", +[](const type& t, const size_t i) { - // bounds_check(t, i); + bounds_check(t, i); return t[i]; }) .def( @@ -106,17 +108,25 @@ namespace py_bindings { }) .def( "__setitem__", +[](type& t, const size_t i, const Real v) { - // bounds_check(t, i); + bounds_check(t, i); t[i] = v; }); + static const auto printer = [](type const &t) { + // Blaze's default printing adds extra lines and spaces which is + // not what we want + std::ostringstream os; + sequence_print_helper(os, t.begin(), t.end()); + return os.str(); + }; + // Need __str__ for converting to string py_vector - // .def("__str__") + .def("__str__", +printer) // repr allows you to output the object in an interactive python // terminal using obj to get the "string REPResenting the object". - // .def("__repr__") - // .def(py::self += py::self) + .def("__repr__", +printer) + .def(py::self += py::self) // Need to do math explicitly converting to DataVector because we don't // want to represent all the possible expression template types .def( diff --git a/backend/src/Utilities/PrintHelpers.hpp b/backend/src/Utilities/PrintHelpers.hpp new file mode 100644 index 000000000..b2134f5a2 --- /dev/null +++ b/backend/src/Utilities/PrintHelpers.hpp @@ -0,0 +1,72 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#pragma once + +#include +#include +#include +#include +#include + +#include "Utilities/StlStreamDeclarations.hpp" + +template +void sequence_print_helper(std::ostream& out, ForwardIt&& begin, + ForwardIt&& end, Func f) noexcept { + out << "("; + if (begin != end) { + while (true) { + f(out, begin++); + if (begin == end) { + break; + } + out << ","; + } + } + out << ")"; +} + +/*! + * \ingroup UtilitiesGroup + * \brief Prints all the items as a comma separated list surrounded by parens. + */ +template +void sequence_print_helper(std::ostream& out, ForwardIt&& begin, + ForwardIt&& end) noexcept { + sequence_print_helper(out, std::forward(begin), + std::forward(end), + [](std::ostream& os, const ForwardIt& it) noexcept { + using ::operator<<; + os << *it; + }); +} + +//@{ +/*! + * \ingroup UtilitiesGroup + * Like sequence_print_helper, but sorts the string representations. + */ +template +void unordered_print_helper(std::ostream& out, ForwardIt&& begin, + ForwardIt&& end, Func f) noexcept { + std::vector entries; + while (begin != end) { + std::ostringstream ss; + f(ss, begin++); + entries.push_back(ss.str()); + } + std::sort(entries.begin(), entries.end()); + sequence_print_helper(out, entries.begin(), entries.end()); +} + +template +void unordered_print_helper(std::ostream& out, ForwardIt&& begin, + ForwardIt&& end) noexcept { + unordered_print_helper(out, std::forward(begin), + std::forward(end), + [](std::ostream& os, const ForwardIt& it) noexcept { + using ::operator<<; + os << *it; + }); +} diff --git a/backend/src/Utilities/Requires.hpp b/backend/src/Utilities/Requires.hpp new file mode 100644 index 000000000..4a74ce9b1 --- /dev/null +++ b/backend/src/Utilities/Requires.hpp @@ -0,0 +1,70 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +// Source : +// https://raw.githubusercontent.com/sxs-collaboration/spectre/develop/src/Utilities/Requires.hpp + +#pragma once + +/// \file +/// Defines the type alias Requires + +#include + +namespace Requires_detail { + template + struct requires_impl { + using template_error_type_failed_to_meet_requirements_on_template_parameters = + std::nullptr_t; + }; + + template <> + struct requires_impl {}; +} // namespace Requires_detail + +/*! + * \ingroup UtilitiesGroup + * \brief Express requirements on the template parameters of a function or + * class, replaces `std::enable_if_t` + * + * Replacement for `std::enable_if_t` and Concepts for expressing requirements + * on template parameters. This does not require merging of the Concepts + * TS (whose merit is debatable) and provides an "error message" if substitution + * of a template parameter failed. Specifically, the compiler error will contain + * "template_error_type_failed_to_meet_requirements_on_template_parameters", + * aiding the user of a function or class in tracking down the list of + * requirements on the deduced type. + * + * For example, if a function `foo` is defined as: + * \snippet Utilities/Test_Requires.cpp foo_definition + * then calling the function with a list, `foo(std::list{});` results in + * the following compilation error from clang: + * \code + * ./tests/Unit/Utilities/Test_Requires.cpp:29:3: error: no matching function + * for call to 'foo' + * foo(std::list{}); + * ^~~ + * ./tests/Unit/Utilities/Test_Requires.cpp:15:13: note: candidate + * template ignored: substitution failure [with T = std::__1::list >]: no type named + * 'template_error_type_failed_to_meet_requirements_on_template_parameters' + * in 'Requires_detail::requires_impl' + * std::string foo(const T&) { + * ^ + * 1 error generated. + * \endcode + * + * Here is an example of how write function overloads using `Requires` or to + * express constraints on the template parameters: + * \snippet Utilities/Test_Requires.cpp function_definitions + * + * \note + * Using `Requires` is safer than using `std::enable_if_t` because the + * nested type alias is of type `std::nullptr_t` and so usage is always: + * \code + * template = nullptr> + * \endcode + */ +template +using Requires = typename Requires_detail::requires_impl< + B>::template_error_type_failed_to_meet_requirements_on_template_parameters; diff --git a/backend/src/Utilities/StlStreamDeclarations.hpp b/backend/src/Utilities/StlStreamDeclarations.hpp new file mode 100644 index 000000000..3df51f5bb --- /dev/null +++ b/backend/src/Utilities/StlStreamDeclarations.hpp @@ -0,0 +1,76 @@ +// Reused from SpECTRE : https://spectre-code.org/ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#pragma once + +// Suppress warnings from this header since they can occur from not including +// StdHelpers later. This header is used only in TypeTraits.hpp so that +// is_streamable works correctly for STL types that have stream operators +// defined. +#ifdef __GNUC__ +#pragma GCC system_header +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Utilities/Requires.hpp" + +namespace tt { + template + struct is_streamable; +} // namespace tt + +template +std::ostream& operator<<(std::ostream& os, const std::list& v) noexcept; + +template +std::ostream& operator<<(std::ostream& os, const std::vector& v) noexcept; + +template +std::ostream& operator<<(std::ostream& os, const std::deque& v) noexcept; + +template +std::ostream& operator<<(std::ostream& os, const std::array& a) noexcept; + +template +std::ostream& operator<<(std::ostream& os, + const std::tuple& t) noexcept; + +template +inline std::ostream& operator<<(std::ostream& os, + const std::unordered_map& m) noexcept; + +template +inline std::ostream& operator<<(std::ostream& os, + const std::map& m) noexcept; + +template +std::ostream& operator<<(std::ostream& os, + const std::unordered_set& v) noexcept; + +template +inline std::ostream& operator<<(std::ostream& os, + const std::set& v) noexcept; + +template ::value> = nullptr> +std::ostream& operator<<(std::ostream& os, + const std::unique_ptr& t) noexcept; + +template ::value> = nullptr> +std::ostream& operator<<(std::ostream& os, + const std::shared_ptr& t) noexcept; + +template +std::ostream& operator<<(std::ostream& os, const std::pair& t) noexcept; From 2e34cdd2030fbb5a28e6bfc8d9d75967d25f66f1 Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Wed, 10 Jul 2024 21:52:33 -0500 Subject: [PATCH 020/121] feat: Add bound check during blaze setter --- backend/README.md | 9 +++ .../src/Utilities/Math/Python/BlazeMatrix.cpp | 6 +- .../src/Utilities/Math/Python/BlazeTensor.cpp | 11 ++- .../src/Utilities/Math/Python/BlazeVector.cpp | 3 +- .../src/Utilities/Math/Python/BoundChecks.hpp | 67 +++++++++++++++++++ 5 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 backend/src/Utilities/Math/Python/BoundChecks.hpp diff --git a/backend/README.md b/backend/README.md index 33bbee46f..506dfeb2f 100644 --- a/backend/README.md +++ b/backend/README.md @@ -33,3 +33,12 @@ For benchmarking various `matmul` implementations, run ``` python3 backend/benchmarking/matmul.py ``` + +## Contributed By + +- Tejaswin Parthasarathy (Teja) +- [Seung Hyun Kim](https://github.com/skim0119) +- Ankith Pai +- [Yashraj Bhosale](https://github.com/bhosale2) +- Arman Tekinalp +- Songyuan Cui diff --git a/backend/src/Utilities/Math/Python/BlazeMatrix.cpp b/backend/src/Utilities/Math/Python/BlazeMatrix.cpp index 4ebe13deb..2834471ab 100644 --- a/backend/src/Utilities/Math/Python/BlazeMatrix.cpp +++ b/backend/src/Utilities/Math/Python/BlazeMatrix.cpp @@ -2,13 +2,11 @@ // Includes //****************************************************************************** -// -// #include "PythonBindings/BoundChecks.hpp" -// #include "Utilities/DefineTypes.h" #include "Utilities/MakeString.hpp" // #include "Utilities/Math/Python/SliceHelpers.hpp" +#include "Utilities/Math/Python/BoundChecks.hpp" // #include #include @@ -99,7 +97,7 @@ namespace py_bindings { const Real val) { matrix_bounds_check(self, std::get<0>(x), std::get<1>(x)); self(std::get<0>(x), std::get<1>(x)) = val; - }); + }); } //**************************************************************************** diff --git a/backend/src/Utilities/Math/Python/BlazeTensor.cpp b/backend/src/Utilities/Math/Python/BlazeTensor.cpp index 9ed4d01c4..779952b77 100644 --- a/backend/src/Utilities/Math/Python/BlazeTensor.cpp +++ b/backend/src/Utilities/Math/Python/BlazeTensor.cpp @@ -3,12 +3,11 @@ //****************************************************************************** -// #include "PythonBindings/BoundChecks.hpp" -// #include "Utilities/DefineTypes.h" #include "Utilities/MakeString.hpp" // #include "Utilities/Math/Python/SliceHelpers.hpp" +#include "Utilities/Math/Python/BoundChecks.hpp" // #include #include @@ -82,8 +81,8 @@ namespace py_bindings { "__getitem__", +[](const type& self, const std::tuple& x) { - // tensor_bounds_check(self, std::get<0>(x), std::get<1>(x), - // std::get<2>(x)); + tensor_bounds_check(self, std::get<0>(x), std::get<1>(x), + std::get<2>(x)); return self(std::get<0>(x), std::get<1>(x), std::get<2>(x)); }) .def( @@ -96,8 +95,8 @@ namespace py_bindings { +[](type& self, const std::tuple& x, const Real val) { - // tensor_bounds_check(self, std::get<0>(x), std::get<1>(x), - // std::get<2>(x)); + tensor_bounds_check(self, std::get<0>(x), std::get<1>(x), + std::get<2>(x)); self(std::get<0>(x), std::get<1>(x), std::get<2>(x)) = val; }) // Need __str__ for converting to string/printing diff --git a/backend/src/Utilities/Math/Python/BlazeVector.cpp b/backend/src/Utilities/Math/Python/BlazeVector.cpp index eed3afa66..1d9494ac1 100644 --- a/backend/src/Utilities/Math/Python/BlazeVector.cpp +++ b/backend/src/Utilities/Math/Python/BlazeVector.cpp @@ -2,13 +2,12 @@ // Includes //****************************************************************************** -// #include "PythonBindings/BoundChecks.hpp" // #include "Utilities/DefineTypes.h" -// #include "Utilities/PrettyType.hpp" #include "Utilities/PrintHelpers.hpp" // #include "Utilities/Math/Python/SliceHelpers.hpp" +#include "Utilities/Math/Python/BoundChecks.hpp" // #include #include diff --git a/backend/src/Utilities/Math/Python/BoundChecks.hpp b/backend/src/Utilities/Math/Python/BoundChecks.hpp new file mode 100644 index 000000000..84f912457 --- /dev/null +++ b/backend/src/Utilities/Math/Python/BoundChecks.hpp @@ -0,0 +1,67 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +#include +#include + +#include "Utilities/PrettyType.hpp" + +namespace py_bindings { + + //**************************************************************************** + /*! \brief Check if a vector-like object access is in bounds. Throws + * std::runtime_error if it is not. + * \ingroup python_bindings + */ + template + void bounds_check(const T& t, const std::size_t i) { + if (i >= t.size()) { + throw std::runtime_error{"Out of bounds access (" + std::to_string(i) + + ") into " + pretty_type::name() + + " of size " + std::to_string(t.size())}; + } + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check if a matrix-like object access is in bounds. Throws + * std::runtime_error if it is not. + * \ingroup python_bindings + */ + template + void matrix_bounds_check(const T& matrix, const std::size_t row, + const std::size_t column) { + if (row >= matrix.rows() or column >= matrix.columns()) { + throw std::runtime_error{"Out of bounds access (" + std::to_string(row) + + ", " + std::to_string(column) + + ") into Matrix of size (" + + std::to_string(matrix.rows()) + ", " + + std::to_string(matrix.columns()) + ")"}; + } + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check if a tensor-like object access is in bounds. Throws + * std::runtime_error if it is not. + * \ingroup python_bindings + */ + template + void tensor_bounds_check(const T& tensor, const std::size_t page, + const std::size_t row, const std::size_t column) { + if (page >= tensor.pages() or row >= tensor.rows() or + column >= tensor.columns()) { + throw std::runtime_error{ + "Out of bounds access (" + std::to_string(page) + ", " + + std::to_string(row) + ", " + std::to_string(column) + + ") into Tensor of size (" + std::to_string(tensor.pages()) + ", " + + std::to_string(tensor.rows()) + ", " + + std::to_string(tensor.columns()) + ")"}; + } + } + //**************************************************************************** + +} // namespace py_bindings From 99ff10f83cb7a4025e6d2bff3e79404c752e3d0c Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Wed, 10 Jul 2024 22:03:23 -0500 Subject: [PATCH 021/121] Add default simulator frames definition --- backend/src/Simulator/Frames.hpp | 95 ++++++++++++++++ .../src/Simulator/Frames/EulerianFrame.hpp | 90 +++++++++++++++ .../src/Simulator/Frames/LagrangianFrame.hpp | 103 ++++++++++++++++++ .../Simulator/Frames/RotationConvention.hpp | 27 +++++ backend/src/Utilities/Math/Types.hpp | 13 --- backend/src/Utilities/Math/Vec3.hpp | 13 --- backend/src/Utilities/NonCreatable.hpp | 77 +++++++++++++ 7 files changed, 392 insertions(+), 26 deletions(-) create mode 100644 backend/src/Simulator/Frames.hpp create mode 100644 backend/src/Simulator/Frames/EulerianFrame.hpp create mode 100644 backend/src/Simulator/Frames/LagrangianFrame.hpp create mode 100644 backend/src/Simulator/Frames/RotationConvention.hpp create mode 100644 backend/src/Utilities/NonCreatable.hpp diff --git a/backend/src/Simulator/Frames.hpp b/backend/src/Simulator/Frames.hpp new file mode 100644 index 000000000..2e46a78f9 --- /dev/null +++ b/backend/src/Simulator/Frames.hpp @@ -0,0 +1,95 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Simulator/Frames/EulerianFrame.hpp" +#include "Simulator/Frames/LagrangianFrame.hpp" +#include "Utilities/Math/Vec3.hpp" + +namespace elastica { + + // ? + using EulerianFrame = detail::EulerianFrame; + using detail::cast_along; + using LagrangianFrame = detail::LagrangianFrame; + // ? + + //**************************************************************************** + /*!\brief Gets a unit vector along an Eulerian direction + * \ingroup simulator + * + * \details + * Gets the unit vector along a direction `Dir` of an Eulerian Frame + * + * \param dir Direction along which to get a unit vector + */ + inline constexpr auto get_unit_vector_along( + EulerianFrame::DirectionType dir) noexcept -> Vec3 { + return { + cast_along(dir) == 0 ? 1.0 : 0.0, + cast_along(dir) == 1 ? 1.0 : 0.0, + cast_along(dir) == 2 ? 1.0 : 0.0, + }; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Gets a unit vector along an Eulerian direction + * \ingroup simulator + * + * \details + * Gets the unit vector along a direction `Dir` of an Eulerian Frame + * + * \tparam Dir Direction along which to get a unit vector + */ + template + inline constexpr auto get_unit_vector_along() noexcept -> Vec3 { + return get_unit_vector_along(Dir); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Gets a unit vector along a Lagrangian direction + * \ingroup simulator + * + * \details + * Gets the unit vector along a direction `Dir` of an Lagrangian Frame + * + * \example + * \snippet Test_LagrangianFrame.cpp vector_along_eg + * + * \param dir Direction along which to get a unit vector + */ + inline constexpr auto get_unit_vector_along( + LagrangianFrame::DirectionType dir) noexcept -> Vec3 { + return { + cast_along(dir) == 0 ? 1.0 : 0.0, + cast_along(dir) == 1 ? 1.0 : 0.0, + cast_along(dir) == 2 ? 1.0 : 0.0, + }; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Gets a unit vector along a Lagrangian direction + * \ingroup simulator + * + * \details + * Gets the unit vector along a direction `Dir` of an Lagrangian Frame + * + * \tparam Dir Direction along which to get a unit vector + */ + template + inline constexpr auto get_unit_vector_along() noexcept -> Vec3 { + return get_unit_vector_along(Dir); + } + //**************************************************************************** + + struct Frames { + static_assert(LagrangianFrame::Dimension == EulerianFrame::Dimension, + "Wrong dimension configuration!"); + static constexpr auto Dimension = LagrangianFrame::Dimension; + }; + +} // namespace elastica diff --git a/backend/src/Simulator/Frames/EulerianFrame.hpp b/backend/src/Simulator/Frames/EulerianFrame.hpp new file mode 100644 index 000000000..c48a35011 --- /dev/null +++ b/backend/src/Simulator/Frames/EulerianFrame.hpp @@ -0,0 +1,90 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include + +#include "Utilities/NonCreatable.hpp" + +namespace elastica { + + namespace detail { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*!\brief Trait defining unique IDs for directions in Eulerian frame + * \ingroup simulator + * + * EulerianFrame is a trait defining the type and number of directions in + * 3D. It serves as a strong type for all directions within the \elastica + * library. + */ + struct EulerianFrame : public NonCreatable { + public: + //**Type definitions****************************************************** + //! The type of direction ID + enum class DirectionType : std::uint8_t { x = 0, y, z, Count }; + //************************************************************************ + + //**Static members******************************************************** + //! unique ID for X direction + static constexpr DirectionType X = DirectionType::x; + //! unique ID for Y direction + static constexpr DirectionType Y = DirectionType::y; + //! unique ID for Z direction + static constexpr DirectionType Z = DirectionType::z; + //! Dimension of simulation + static constexpr auto Dimension = + static_cast(DirectionType::Count); + //************************************************************************ + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Casts a Eulerian frame direction into an index value + * \ingroup simulator + * + * \details + * Converts a EulerianFrame::DirectionType into an index value in + * associative arrays. + * + * \example + * \snippet Test_EulerianFrame.cpp cast_along_eg + * + * \param dir A Direction to cast as index to + */ + inline constexpr auto cast_along(EulerianFrame::DirectionType dir) + -> std::size_t { + return static_cast(dir); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Casts a Eulerian frame direction into an index value + * \ingroup simulator + * + * \details + * Converts a EulerianFrame::DirectionType into an index value in + * associative arrays. + * + * \example + * \snippet Test_EulerianFrame.cpp cast_along_template_eg + * + * \tparam Dir A Direction to cast as index to + */ + template + inline constexpr auto cast_along() -> std::size_t { + return cast_along(Dir); + } + //************************************************************************** + + } // namespace detail + +} // namespace elastica diff --git a/backend/src/Simulator/Frames/LagrangianFrame.hpp b/backend/src/Simulator/Frames/LagrangianFrame.hpp new file mode 100644 index 000000000..d6976364a --- /dev/null +++ b/backend/src/Simulator/Frames/LagrangianFrame.hpp @@ -0,0 +1,103 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include + +#include "Simulator/Frames/RotationConvention.hpp" + +#include "Utilities/NonCreatable.hpp" + +namespace elastica { + + namespace detail { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Trait defining unique IDs for different directions + * \ingroup domain + * + * LagrangianFrame is a trait defining the type and number of directions in + * 3D, in a convected frame. It serves as a strong type for all Lagrangian + * directions within the \elastica library. + */ + struct LagrangianFrame : public NonCreatable { + public: + //**Type definitions****************************************************** + //! The type of direction ID + enum class DirectionType : std::uint8_t { d1 = 0, d2, d3, Count }; + //************************************************************************ + + //**Static members******************************************************** + //! unique ID for D1 direction + static constexpr DirectionType D1 = DirectionType::d1; + //! unique ID for D2 direction + static constexpr DirectionType D2 = DirectionType::d2; + //! unique ID for D3 direction + static constexpr DirectionType D3 = DirectionType::d3; + //! unique ID for the normal direction + static constexpr DirectionType Normal = + DirectionType(RotationConvention::Normal); + //! unique ID for the binormal direction + static constexpr DirectionType Binormal = + DirectionType(RotationConvention::Binormal); + //! unique ID for the tangent direction + static constexpr DirectionType Tangent = + DirectionType(RotationConvention::Tangent); + //! Dimension of simulation + static constexpr auto Dimension = + static_cast(DirectionType::Count); + //************************************************************************ + }; + /*! \endcond */ + //************************************************************************** + + //************************************************************************** + /*!\brief Casts a Lagrangian frame direction into an index value + * \ingroup simulator + * + * \details + * Converts a LagrangianFrame::DirectionType into an index value for use in + * generic code or associative arrays. + * + * \example + * \snippet Test_LagrangianFrame.cpp cast_along_eg + * + * \param dir A Direction to cast as index to + */ + inline constexpr auto cast_along(LagrangianFrame::DirectionType dir) + -> std::size_t { + return static_cast(dir); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Casts a Lagrangian frame direction into an index value + * \ingroup simulator + * + * \details + * Converts a LagrangianFrame::DirectionType into an index value for use in + * generic code or associative arrays. + * + * \example + * \snippet Test_LagrangianFrame.cpp cast_along_template_eg + * + * \tparam Dir A Direction to cast as index to + */ + template + inline constexpr auto cast_along() -> std::size_t { + return cast_along(Dir); + } + //************************************************************************** + + } // namespace detail + +} // namespace elastica diff --git a/backend/src/Simulator/Frames/RotationConvention.hpp b/backend/src/Simulator/Frames/RotationConvention.hpp new file mode 100644 index 000000000..724dfb3f9 --- /dev/null +++ b/backend/src/Simulator/Frames/RotationConvention.hpp @@ -0,0 +1,27 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include + +namespace elastica { + + //************************************************************************** + /*!\brief The rotation convention followed by \elastica + * \ingroup utils + * + * \details + * TODO + */ + struct RotationConvention { + //! unique ID for the normal direction + static constexpr std::uint8_t Normal = 0U; + //! unique ID for the binormal direction + static constexpr std::uint8_t Binormal = 1U; + //! unique ID for the tangent direction + static constexpr std::uint8_t Tangent = 2U; + }; + //**************************************************************************** + +} // namespace elastica diff --git a/backend/src/Utilities/Math/Types.hpp b/backend/src/Utilities/Math/Types.hpp index 6c84669e7..3357c1774 100644 --- a/backend/src/Utilities/Math/Types.hpp +++ b/backend/src/Utilities/Math/Types.hpp @@ -1,16 +1,3 @@ -//============================================================================== -/*! -// \file -// \brief Types belonging to the Math module -// -// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved -// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved -// -// Distributed under the MIT License. -// See LICENSE.txt for details. -*/ -//============================================================================== - #pragma once //****************************************************************************** diff --git a/backend/src/Utilities/Math/Vec3.hpp b/backend/src/Utilities/Math/Vec3.hpp index f73a7d5be..f1bf13b31 100644 --- a/backend/src/Utilities/Math/Vec3.hpp +++ b/backend/src/Utilities/Math/Vec3.hpp @@ -1,16 +1,3 @@ -//============================================================================== -/*! -// \file -// \brief Header for a 3D vector for use in \elastica interface -// -// Copyright (C) 2020-2020 Tejaswin Parthasarathy - All Rights Reserved -// Copyright (C) 2020-2020 MattiaLab - All Rights Reserved -// -// Distributed under the MIT License. -// See LICENSE.txt for details. -*/ -//============================================================================== - #pragma once //****************************************************************************** diff --git a/backend/src/Utilities/NonCreatable.hpp b/backend/src/Utilities/NonCreatable.hpp new file mode 100644 index 000000000..ca2caa549 --- /dev/null +++ b/backend/src/Utilities/NonCreatable.hpp @@ -0,0 +1,77 @@ +//============================================================================== +/*! + * From: file pe/util/NonCreatable.h + * \brief Base class for for non-creatable (static) classes + * + * Copyright (C) 2009 Klaus Iglberger + * + * This file is part of pe. + * + * pe is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * pe is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * pe. If not, see . + */ +//============================================================================== + +#pragma once + +namespace elastica { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Base class for non-creatable (static) classes. + // \ingroup util + // + // The NonCreatable class is intended to work as a base class for + // non-creatable classes, i.e. classes that cannot be instantiated and + // exclusively offer static functions/data. Both the standard as well as the + // copy constructor and the copy assignment operator are deleted + // and left undefined in order to prohibit the instantiation of objects of + // derived classes. + // + // \note + // It is not necessary to publicly derive from this class. It is sufficient to + // derive privately to prevent the instantiation of the derived class. + // + // \example + \code + class A : private NonCreatable + { ... }; + \endcode + // + // \see NonCopyable + */ + class NonCreatable { + public: + //**Constructors and copy assignment operator******************************* + /*!\name Constructors and copy assignment operator */ + //@{ + //! Constructor (private & deleted) + NonCreatable() = delete; + //! Copy constructor (private & deleted) + NonCreatable(const NonCreatable&) = delete; + //! Move constructor (private & deleted) + NonCreatable(NonCreatable&&) = delete; + //! Copy assignment operator (private & deleted) + NonCreatable& operator=(const NonCreatable&) = delete; + //! Move assignment operator (private & deleted) + NonCreatable& operator=(NonCreatable&&) = delete; + //@} + //************************************************************************** + }; + //**************************************************************************** + +} // namespace elastica From d03eb976e1516744c44fed5d74166bf0d3bfdb65 Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Wed, 10 Jul 2024 23:13:09 -0500 Subject: [PATCH 022/121] binding: elastica tags --- backend/src/Systems/Block/TypeTraits.hpp | 26 ++ backend/src/Systems/Block/Types.hpp | 10 + backend/src/Systems/Python/BindTags.cpp | 27 ++ backend/src/Systems/Python/BindTags.hpp | 46 +++ backend/src/Systems/Python/Bindings.cpp | 17 + backend/src/Systems/Systems.hpp | 60 +++ backend/src/Systems/Types.hpp | 10 + backend/src/Utilities/Math/Zero.hpp | 50 +++ backend/src/Utilities/TMPL.hpp | 444 +++++++++++++++++++++++ 9 files changed, 690 insertions(+) create mode 100644 backend/src/Systems/Block/TypeTraits.hpp create mode 100644 backend/src/Systems/Block/Types.hpp create mode 100644 backend/src/Systems/Python/BindTags.cpp create mode 100644 backend/src/Systems/Python/BindTags.hpp create mode 100644 backend/src/Systems/Python/Bindings.cpp create mode 100644 backend/src/Systems/Systems.hpp create mode 100644 backend/src/Systems/Types.hpp create mode 100644 backend/src/Utilities/Math/Zero.hpp create mode 100644 backend/src/Utilities/TMPL.hpp diff --git a/backend/src/Systems/Block/TypeTraits.hpp b/backend/src/Systems/Block/TypeTraits.hpp new file mode 100644 index 000000000..62da94b0d --- /dev/null +++ b/backend/src/Systems/Block/TypeTraits.hpp @@ -0,0 +1,26 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +/// +#include "Types.hpp" +/// +#include "Systems/Block/Block/Aliases.hpp" +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/Aliases.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" + +//============================================================================== +// +// DOXYGEN DOCUMENTATION +// +//============================================================================== + +//****************************************************************************** +/*!\defgroup block_tt Block Type Traits + * \ingroup blocks + * \brief Type traits for blocks + */ +//****************************************************************************** diff --git a/backend/src/Systems/Block/Types.hpp b/backend/src/Systems/Block/Types.hpp new file mode 100644 index 000000000..baa7f944e --- /dev/null +++ b/backend/src/Systems/Block/Types.hpp @@ -0,0 +1,10 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +/// +#include "Systems/Block/Block/Types.hpp" +#include "Systems/Block/BlockVariables/Types.hpp" +/// diff --git a/backend/src/Systems/Python/BindTags.cpp b/backend/src/Systems/Python/BindTags.cpp new file mode 100644 index 000000000..3a56bbeda --- /dev/null +++ b/backend/src/Systems/Python/BindTags.cpp @@ -0,0 +1,27 @@ +//****************************************************************************** +// Includes +//****************************************************************************** + +// +#include +// +#include "Systems/Systems.hpp" +// +#include "Systems/Python/BindTags.hpp" +// +#include "Utilities/TMPL.hpp" + +namespace py = pybind11; + +namespace py_bindings { + + //**************************************************************************** + /*!\brief Helps bind tags for all physical systems in \elastica + * \ingroup python_bindings + */ + void bind_tags(py::module& m) { // NOLINT + bind_tags<::elastica::PhysicalSystemPlugins>(m); + } + //**************************************************************************** + +} // namespace py_bindings diff --git a/backend/src/Systems/Python/BindTags.hpp b/backend/src/Systems/Python/BindTags.hpp new file mode 100644 index 000000000..43ebca155 --- /dev/null +++ b/backend/src/Systems/Python/BindTags.hpp @@ -0,0 +1,46 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +// +#include "Systems/Block/TypeTraits.hpp" +// +// #include "Utilities/PrettyType.hpp" +// +#include +// +#include + +namespace py_bindings { + + //**************************************************************************** + /*!\brief Helps bind (unique) tags of Plugins in \elastica + * \ingroup python_bindings + * + * \details + * + * + * \param m A python module + */ + template + void bind_tags(pybind11::module& m) { + using AllVariables = tmpl::flatten< + tmpl::transform>>; + using UniqueTags = tmpl::remove_duplicates>>; + + // first define variables as a read only property for named evaluation + tmpl::for_each([&](auto v) { + namespace py = pybind11; + using Tag = tmpl::type_from; + + // FIXME: avoiding pretty_type for now. + // std::string tag_name = pretty_type::short_name(); + std::string tag_name = typeid(v).name(); + py::class_(m, ("_" + tag_name).c_str(), + ("Symbol corresponding to C++ tag " + tag_name).c_str()); + }); + } + +} // namespace py_bindings diff --git a/backend/src/Systems/Python/Bindings.cpp b/backend/src/Systems/Python/Bindings.cpp new file mode 100644 index 000000000..de7d08ac4 --- /dev/null +++ b/backend/src/Systems/Python/Bindings.cpp @@ -0,0 +1,17 @@ +//****************************************************************************** +// Includes +//****************************************************************************** +#include + +namespace py = pybind11; + +namespace py_bindings { + void bind_tags(py::module& m); // NOLINT +} // namespace py_bindings + +PYBIND11_MODULE(_PyTags, m) { // NOLINT + m.doc() = R"pbdoc( + Bindings for Elastica++ tag types + )pbdoc"; + py_bindings::bind_tags(m); +} diff --git a/backend/src/Systems/Systems.hpp b/backend/src/Systems/Systems.hpp new file mode 100644 index 000000000..c2691208b --- /dev/null +++ b/backend/src/Systems/Systems.hpp @@ -0,0 +1,60 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +// +#include "Systems/Types.hpp" +// +#include "Systems/Protocols.hpp" +#include "Systems/Tags.hpp" +// +#include "Systems/Block.hpp" +#include "Systems/CosseratRods.hpp" +// #include "Systems/RigidBody.hpp" +#include "Systems/States/States.hpp" +// +#include "Utilities/TMPL.hpp" + +//============================================================================== +// +// DOXYGEN DOCUMENTATION +// +//============================================================================== + +//****************************************************************************** +/*!\defgroup systems Physical systems + * \brief Physical systems (that occupy space and evolve in time) in \elastica + */ +//****************************************************************************** + +namespace elastica { + + //**************************************************************************** + /*!\brief All implemented CosseratRod plugins in \elastica + * \ingroup systems + */ + using CosseratRodPlugins = + tmpl::list<::elastica::cosserat_rod::CosseratRod, + ::elastica::cosserat_rod::CosseratRodWithoutDamping>; + //**************************************************************************** + + //**************************************************************************** + /*!\brief All implemented RigidBody plugins in \elastica + * \ingroup systems + */ + // using RigidBodyPlugins = tmpl::list<::elastica::rigid_body::Sphere>; + //**************************************************************************** + + //**************************************************************************** + /*!\brief All implemented physical system plugins in \elastica + * \ingroup systems + * + * \see elastica::CosseratRodPlugins, elastica::RigidBodyPlugins + */ + using PhysicalSystemPlugins = + tmpl::append; + // tmpl::append; + //**************************************************************************** + +} // namespace elastica diff --git a/backend/src/Systems/Types.hpp b/backend/src/Systems/Types.hpp new file mode 100644 index 000000000..35695934a --- /dev/null +++ b/backend/src/Systems/Types.hpp @@ -0,0 +1,10 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/Block/Types.hpp" +#include "Systems/CosseratRods/Types.hpp" +// #include "Systems/RigidBody/Types.hpp" +#include "Systems/States/Types.hpp" +#include "Systems/common/Types.hpp" diff --git a/backend/src/Utilities/Math/Zero.hpp b/backend/src/Utilities/Math/Zero.hpp new file mode 100644 index 000000000..3aa3eea19 --- /dev/null +++ b/backend/src/Utilities/Math/Zero.hpp @@ -0,0 +1,50 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +#include + +#include "Utilities/Math/Vec3.hpp" + +namespace elastica { + + //**************************************************************************** + /*!\brief Checks for a zero number + */ + inline auto is_zero(real_t real_number) noexcept -> bool { + return ::blaze::isZero(real_number); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Checks for a zero vector + */ + inline auto is_zero(const Vec3& vec) noexcept -> bool { + return ::blaze::isZero(vec); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Checks for a zero length vector + * + * Checks if the length of a vector is zero or as close to zero that it + * can not be distinguished form zero + */ + inline auto is_zero_length(const Vec3& vec) noexcept -> bool { + // return vec.sqrLength() < Limits::fpuAccuracy(); + return ::blaze::sqrLength(vec) < ::blaze::accuracy; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Returns a zero vector + */ + inline auto zero3() noexcept -> Vec3 { + return Vec3{static_cast(0.0), static_cast(0.0), + static_cast(0.0)}; + } + //**************************************************************************** + +} // namespace elastica diff --git a/backend/src/Utilities/TMPL.hpp b/backend/src/Utilities/TMPL.hpp new file mode 100644 index 000000000..8898752c1 --- /dev/null +++ b/backend/src/Utilities/TMPL.hpp @@ -0,0 +1,444 @@ +#pragma once + +// Since this header only wraps brigand and several additions to it we mark +// it as a system header file so that clang-tidy ignores it. +#ifdef __GNUC__ +#pragma GCC system_header +#endif + +#define BRIGAND_NO_BOOST_SUPPORT +#include +#include + +#include "Requires.hpp" +//#include "TypeTraits.hpp" + +namespace tmpl = brigand; + +// Use spectre's TMPL + +namespace brigand { + /// Check if a typelist contains an item. + template + using list_contains = + tmpl::found>>; + + template + constexpr const bool list_contains_v = list_contains::value; + + /// Obtain the elements of `Sequence1` that are not in `Sequence2`. + template + using list_difference = + fold>; + +} // namespace brigand + +namespace brigand { + namespace detail { + template + struct remove_duplicates_helper { + using type = typename std::conditional< + std::is_same, no_such_type_>::value, push_back, + S>::type; + }; + } // namespace detail + + template + using remove_duplicates = + fold, detail::remove_duplicates_helper<_state, _element>>; +} // namespace brigand + +namespace brigand { + template + using cartesian_product = reverse_fold< + list, list>, + lazy::join, + defer>>>>>>>>>; + +} + +namespace brigand { + template + struct conditional; + + template <> + struct conditional { + template + using type = T; + }; + + template <> + struct conditional { + template + using type = F; + }; + + template + using conditional_t = typename conditional::template type; +} // namespace brigand + +/*! + * \ingroup UtilitiesGroup + * \brief Allows zero-cost unordered expansion of a parameter + * + * \details + * Expands a parameter pack, typically useful for runtime evaluation via a + * Callable such as a lambda, function, or function object. For example, + * an unordered transform of a std::tuple can be implemented as: + * \snippet Utilities/Test_TMPL.cpp expand_pack_example + * + * \see tuple_fold tuple_counted_fold tuple_transform std::tuple + * EXPAND_PACK_LEFT_TO_RIGHT + */ +template +constexpr void expand_pack(Ts&&...) noexcept {} + +/*! + * \ingroup UtilitiesGroup + * \brief Expand a parameter pack evaluating the terms from left to right. + * + * The parameter pack inside the argument to the macro must not be expanded + * since the macro will do the expansion correctly for you. In the below example + * a parameter pack of `std::integral_constant` is passed to the + * function. The closure `lambda` is used to sum up the values of all the `Ts`. + * Note that the `Ts` passed to `EXPAND_PACK_LEFT_TO_RIGHT` is not expanded. + * + * \snippet Utilities/Test_TMPL.cpp expand_pack_left_to_right + * + * \see tuple_fold tuple_counted_fold tuple_transform std::tuple expand_pack + */ +#define EXPAND_PACK_LEFT_TO_RIGHT(...) \ + (void)std::initializer_list { ((void)(__VA_ARGS__), '0')... } + +/*! + * \ingroup UtilitiesGroup + * \brief Returns the first argument of a parameter pack + */ +template +constexpr decltype(auto) get_first_argument(T&& t, Ts&&... /*rest*/) noexcept { + return t; +} + +// namespace tmpl { +// // Types list +// template +// struct list {}; +// +// // Values list +// template +// using integral_list = list...>; +// +// // Empty list +// using empty_sequence = tmpl::list<>; +// +// // Predefined placeholders +// struct _1 {}; +// struct _2 {}; +// +// // Utility to expand a typelist into its variadic constituents +// // and apply a functor/expression on the pack +// namespace detail { +// template class B> +// struct wrap; +// +// template