From 7c4b284d9e12e113fa8e50405abde5713ca3bf3f Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 12 Jul 2024 15:55:40 -0500 Subject: [PATCH 1/3] backup [skip ci] --- .../allocator/allocator_base.hpp | 87 +++++++++++++++++++ include/tensorwrapper/allocator/eigen.hpp | 86 ++++++++++++++++++ include/tensorwrapper/allocator/local.hpp | 20 +++++ .../tensorwrapper/allocator/replicated.hpp | 21 +++++ .../detail_/unique_ptr_utilities.hpp | 36 ++++++++ include/tensorwrapper/layout/tiled.hpp | 14 ++- src/tensorwrapper/allocator/eigen.cpp | 53 +++++++++++ .../tensorwrapper/allocator/eigen.cpp | 50 +++++++++++ .../detail_/unique_ptr_utilities.cpp | 36 ++++++++ .../unit_tests/tensorwrapper/layout/tiled.cpp | 8 ++ 10 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 include/tensorwrapper/allocator/allocator_base.hpp create mode 100644 include/tensorwrapper/allocator/eigen.hpp create mode 100644 include/tensorwrapper/allocator/local.hpp create mode 100644 include/tensorwrapper/allocator/replicated.hpp create mode 100644 include/tensorwrapper/detail_/unique_ptr_utilities.hpp create mode 100644 src/tensorwrapper/allocator/eigen.cpp create mode 100644 tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp create mode 100644 tests/cxx/unit_tests/tensorwrapper/detail_/unique_ptr_utilities.cpp diff --git a/include/tensorwrapper/allocator/allocator_base.hpp b/include/tensorwrapper/allocator/allocator_base.hpp new file mode 100644 index 00000000..854649e4 --- /dev/null +++ b/include/tensorwrapper/allocator/allocator_base.hpp @@ -0,0 +1,87 @@ +#pragma once +#include +#include +#include +#include + +namespace tensorwrapper::allocator { + +/** @brief Common base class for all allocators. + * + * The AllocatorBase class serves as type-erasure and a unified API for all + * allocators. + */ +class AllocatorBase : public detail_::PolymorphicBase { +private: + /// The type of *this + using my_type = AllocatorBase; + + /// The type *this derives from + using my_base_type = detail_::PolymorphicBase; + +public: + /// Type of a view of the runtime system + using runtime_view_type = parallelzone::runtime::RuntimeView; + + /// Type of a mutable reference to the runtime system + using runtime_view_reference = runtime_view_type&; + + /// Type of a read-only reference to the runtime system + using const_runtime_view_reference = const runtime_view_type&; + + /// Type all layouts derive from + using layout_type = layout::Tiled; + + /// Type of a pointer to an object of type layout_type + using layout_pointer = typename layout_type::layout_pointer; + + /// Type all buffers derive from + using buffer_base_type = buffer::BufferBase; + + /// Type of a pointer to an object of type buffer_base_type + using buffer_base_pointer = typename buffer_base_type::buffer_base_pointer; + + // ------------------------------------------------------------------------- + // -- Ctors and assignment + // ------------------------------------------------------------------------- + + explicit AllocatorBase(runtime_view_type rv) : m_rv_(std::move(rv)) {} + + buffer_base_pointer allocate(layout_pointer playout) { + return allocate_(std::move(playout)); + } + + runtime_view_reference runtime() noexcept { return m_rv_; } + + const_runtime_view_reference runtime() const noexcept { return m_rv_; } + + // ------------------------------------------------------------------------- + // -- Utility methods + // ------------------------------------------------------------------------- + + bool operator==(const AllocatorBase& rhs) const noexcept { + return m_rv_ == rhs.m_rv_; + } + + bool operator!=(const AllocatorBase& rhs) const noexcept { + return !((*this) == rhs); + } + +protected: + /** @brief Creates *this so that it uses the same runtime as @p other. + * + * @param[in] other The allocator to make a copy of. + * + * @throw std::bad_alloc if there is a problem allocating the copy. Strong + * throw guarantee. + */ + AllocatorBase(const AllocatorBase& other) = default; + + virtual buffer_base_pointer allocate_(layout_pointer playout) = 0; + +private: + /// The runtime we are allocating memory in + runtime_view_type m_rv_; +}; + +} // namespace tensorwrapper::allocator diff --git a/include/tensorwrapper/allocator/eigen.hpp b/include/tensorwrapper/allocator/eigen.hpp new file mode 100644 index 00000000..ea2a8998 --- /dev/null +++ b/include/tensorwrapper/allocator/eigen.hpp @@ -0,0 +1,86 @@ +#pragma once +#include +#include +#include + +namespace tensorwrapper::allocator { + +/** @brief Used to allocate buffers which rely on Eigen tensors. + * + * @tparam FloatType + */ +template +class Eigen : public Replicated { +private: + /// The type of *this + using my_type = Eigen; + + /// The class *this inherits from + using my_base_type = Replicated; + +public: + // Pull in base class's types + using my_base_type::base_pointer; + using my_base_type::buffer_base_pointer; + using my_base_type::const_base_reference; + using my_base_type::layout_pointer; + + /// Type of a buffer containing an Eigen tensor + using eigen_buffer_type = buffer::Eigen; + + /// Type of a pointer to an eigen_buffer_type object + using eigen_buffer_pointer = std::unique_ptr; + + /// Type of a layout which can be used to create an Eigen tensor + using eigen_layout_type = layout::MonoTile; + + /// Type of a read-only reference to an object of type eigen_layout_type + using const_eigen_layout_reference = const eigen_layout_type&; + + /// Type of a pointer to an eigen_layout_type object + using eigen_layout_pointer = std::unique_ptr; + + // Reuse base class's ctors + using my_base_type::my_base_type; + + eigen_buffer_pointer allocate(const_eigen_layout_reference layout) { + return allocate(std::make_unique(layout)); + } + + eigen_buffer_pointer allocate(eigen_layout_pointer playout); + +protected: + buffer_base_pointer allocate_(layout_pointer playout) override; + + base_pointer clone_() const override { + return std::make_unique(*this); + } + + bool are_equal_(const_base_reference rhs) const noexcept override { + return my_base_type::are_equal_impl_(rhs); + } +}; + +// ----------------------------------------------------------------------------- +// -- Explicit class template declarations +// ----------------------------------------------------------------------------- + +#define DECLARE_EIGEN_ALLOCATOR(RANK) \ + extern template class Eigen; \ + extern template class Eigen + +DECLARE_EIGEN_ALLOCATOR(0); +DECLARE_EIGEN_ALLOCATOR(1); +DECLARE_EIGEN_ALLOCATOR(2); +DECLARE_EIGEN_ALLOCATOR(3); +DECLARE_EIGEN_ALLOCATOR(4); +DECLARE_EIGEN_ALLOCATOR(5); +DECLARE_EIGEN_ALLOCATOR(6); +DECLARE_EIGEN_ALLOCATOR(7); +DECLARE_EIGEN_ALLOCATOR(8); +DECLARE_EIGEN_ALLOCATOR(9); +DECLARE_EIGEN_ALLOCATOR(10); + +#undef DECLARE_EIGEN_ALLOCATOR + +} // namespace tensorwrapper::allocator diff --git a/include/tensorwrapper/allocator/local.hpp b/include/tensorwrapper/allocator/local.hpp new file mode 100644 index 00000000..b07f1726 --- /dev/null +++ b/include/tensorwrapper/allocator/local.hpp @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace tensorwrapper::allocator { + +/** @brief Can create buffers that exist entirely in local memory. + * + * This class is presently a stub that will be filled in later, as needed. + */ +class Local : public AllocatorBase { +private: + /// Type *this inherits from + using my_base_type = AllocatorBase; + +public: + // Pull in base's ctors + using my_base_type::my_base_type; +}; + +} // namespace tensorwrapper::allocator diff --git a/include/tensorwrapper/allocator/replicated.hpp b/include/tensorwrapper/allocator/replicated.hpp new file mode 100644 index 00000000..8336b8bd --- /dev/null +++ b/include/tensorwrapper/allocator/replicated.hpp @@ -0,0 +1,21 @@ +#pragma once +#include + +namespace tensorwrapper::allocator { + +/** @brief Can create buffers that exist entirely in local memory and are + * guaranteed to be the same for all processes. + * + * This class is presently a stub that will be filled in later, as needed. + */ +class Replicated : public Local { +private: + /// Type *this inherits from + using my_base_type = Local; + +public: + // Pull in base's ctors + using my_base_type::my_base_type; +}; + +} // namespace tensorwrapper::allocator diff --git a/include/tensorwrapper/detail_/unique_ptr_utilities.hpp b/include/tensorwrapper/detail_/unique_ptr_utilities.hpp new file mode 100644 index 00000000..4b8a4fa6 --- /dev/null +++ b/include/tensorwrapper/detail_/unique_ptr_utilities.hpp @@ -0,0 +1,36 @@ +#pragma once +#include + +namespace tensorwrapper::detail_ { + +/** @brief Implements a dynamic cast of a unique_ptr. + * + * @tparam T The object type to cast from. + * @tparam U The object type to cast to. + * + * The C++ standard library does not implement dynamic cast for unique pointers + * (because there is no way to do this without two variables thinking they own + * the memory). This function implements dynamic cast by essentially swapping + * the raw pointers in two unique pointers (one of which is a nullptr) when the + * object pointed to by @p pbase can be dynamically casted to @p U. This + * minimizes the time when the single owner violation occurs (and encapsulates + * it to this function). + * + * @param[in,out] pbase The pointer we are dynamic casting. If the cast + * succeeds @p pbase will be set to the nullptr. If the + * cast fails @p pbase will be unchanged. + * + * @return If the cast succeeds a new `std::unique_ptr` object which + * owns the dynamic casted memory originally owned by @p pbase. + * Otherwise a nullptr. + * + * @throw None No throw guarantee. + */ +template +std::unique_ptr dynamic_pointer_cast(std::unique_ptr& pbase) { + auto pderived_raw = dynamic_cast(pbase.get()); + if(pderived_raw) pbase.release(); + return std::unique_ptr(pderived_raw); +} + +} // namespace tensorwrapper::detail_ diff --git a/include/tensorwrapper/layout/tiled.hpp b/include/tensorwrapper/layout/tiled.hpp index e47854f1..763ad818 100644 --- a/include/tensorwrapper/layout/tiled.hpp +++ b/include/tensorwrapper/layout/tiled.hpp @@ -116,9 +116,21 @@ class Tiled : public detail_::PolymorphicBase { */ bool has_shape() const noexcept { return m_shape_ != nullptr; } + /** @brief Provides read-only access to the shape of the layout. + * + * @return A read-only reference to the shape of the layout. + * + * @throw std::runtime_error if *this does not have a shape. Strong throw + * guarantee. + */ + const_shape_reference shape() const { + if(!has_shape()) throw std::runtime_error("Layout's shape not set."); + return *m_shape_; + } + /** @brief Provides read-only access to the symmetry of the layout. * - * @return A red-only reference to the symmetry of the layout. + * @return A read-only reference to the symmetry of the layout. * * @throw None No throw guarantee. */ diff --git a/src/tensorwrapper/allocator/eigen.cpp b/src/tensorwrapper/allocator/eigen.cpp new file mode 100644 index 00000000..9645a88f --- /dev/null +++ b/src/tensorwrapper/allocator/eigen.cpp @@ -0,0 +1,53 @@ +#include +#include + +namespace tensorwrapper::allocator { + +#define TPARAMS template +#define EIGEN Eigen + +TPARAMS +typename EIGEN::eigen_buffer_pointer EIGEN::allocate( + eigen_layout_pointer playout) { + using eigen_tensor_type = typename eigen_buffer_type::tensor_type; + if(playout->shape().rank() != Rank) + throw std::runtime_error("Rank of the layout is not compatible"); + return std::make_unique(eigen_tensor_type(), *playout); +} + +// ----------------------------------------------------------------------------- +// -- Protected methods +// ----------------------------------------------------------------------------- + +TPARAMS +typename EIGEN::buffer_base_pointer EIGEN::allocate_(layout_pointer playout) { + auto pderived = detail_::dynamic_pointer_cast(playout); + if(pderived == nullptr) throw std::runtime_error("Unsupported layout"); + + return allocate(std::move(pderived)); +} + +#undef EIGEN +#undef TPARAMS + +// -- Explicit class template instantiation + +#define DEFINE_EIGEN_ALLOCATOR(RANK) \ + template class Eigen; \ + template class Eigen + +DEFINE_EIGEN_ALLOCATOR(0); +DEFINE_EIGEN_ALLOCATOR(1); +DEFINE_EIGEN_ALLOCATOR(2); +DEFINE_EIGEN_ALLOCATOR(3); +DEFINE_EIGEN_ALLOCATOR(4); +DEFINE_EIGEN_ALLOCATOR(5); +DEFINE_EIGEN_ALLOCATOR(6); +DEFINE_EIGEN_ALLOCATOR(7); +DEFINE_EIGEN_ALLOCATOR(8); +DEFINE_EIGEN_ALLOCATOR(9); +DEFINE_EIGEN_ALLOCATOR(10); + +#undef DEFINE_EIGEN_ALLOCATOR + +} // namespace tensorwrapper::allocator diff --git a/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp b/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp new file mode 100644 index 00000000..40fcfffd --- /dev/null +++ b/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp @@ -0,0 +1,50 @@ +#include "../helpers.hpp" +#include +#include +#include + +using namespace tensorwrapper; + +TEMPLATE_TEST_CASE("EigenAllocator", "", float, double) { + using scalar_alloc_type = allocator::Eigen; + using vector_alloc_type = allocator::Eigen; + using matrix_alloc_type = allocator::Eigen; + using layout_type = typename scalar_alloc_type::eigen_layout_type; + using shape_type = typename shape::Smooth; + using symmetry_type = typename layout_type::symmetry_type; + using sparsity_type = typename layout_type::sparsity_type; + using eigen_buffer_scalar = typename scalar_alloc_type::eigen_buffer_type; + using eigen_scalar = typename eigen_buffer_scalar::tensor_type; + // using eigen_vector = typename vector_alloc_type::eigen_buffer_type; + // using eigen_matrix = typename matrix_alloc_type::eigen_buffer_type; + + parallelzone::runtime::RuntimeView rv; + + symmetry_type g; + sparsity_type sparsity; + layout_type scalar_layout(shape_type{}, g, sparsity); + layout_type vector_layout(shape_type{2}, g, sparsity); + layout_type matrix_layout(shape_type{2, 2}, g, sparsity); + + scalar_alloc_type scalar_alloc(rv); + vector_alloc_type vector_alloc(rv); + matrix_alloc_type matrix_alloc(rv); + + eigen_scalar scalar; + scalar() = 0.0; + eigen_buffer_scalar scalar_corr(scalar, scalar_layout); + + SECTION("Ctor") { + SECTION("runtime") { + REQUIRE(scalar_alloc.runtime() == rv); + REQUIRE(vector_alloc.runtime() == rv); + REQUIRE(matrix_alloc.runtime() == rv); + } + } + + SECTION("allocate(MonoTile)") { + // N.b. allocate doesn't initialize tensor, so only compare layouts + auto pscalar = scalar_alloc.allocate(scalar_layout); + REQUIRE(pscalar->layout().are_equal(scalar_layout)); + } +} diff --git a/tests/cxx/unit_tests/tensorwrapper/detail_/unique_ptr_utilities.cpp b/tests/cxx/unit_tests/tensorwrapper/detail_/unique_ptr_utilities.cpp new file mode 100644 index 00000000..52ecd329 --- /dev/null +++ b/tests/cxx/unit_tests/tensorwrapper/detail_/unique_ptr_utilities.cpp @@ -0,0 +1,36 @@ +#include +#include +#include + +using namespace tensorwrapper::detail_; + +namespace { +struct BaseClass { + virtual ~BaseClass() = default; +}; +struct DerivedClass : public BaseClass { + int x = 2; +}; +} // namespace + +TEST_CASE("dynamic_pointer_cast") { + auto pderived = std::make_unique(); + DerivedClass* pderived_corr = pderived.get(); + + std::unique_ptr pbase(std::move(pderived)); + + SECTION("Good cast") { + auto pdowncast = dynamic_pointer_cast(pbase); + + REQUIRE(pbase.get() == nullptr); + REQUIRE(pdowncast.get() == pderived_corr); + } + + SECTION("Bad cast") { + BaseClass* pbase_corr = pbase.get(); + auto pbadcast = dynamic_pointer_cast>(pbase); + + REQUIRE(pbase.get() == pbase_corr); + REQUIRE(pbadcast.get() == nullptr); + } +} diff --git a/tests/cxx/unit_tests/tensorwrapper/layout/tiled.cpp b/tests/cxx/unit_tests/tensorwrapper/layout/tiled.cpp index 8780c584..ebe42d73 100644 --- a/tests/cxx/unit_tests/tensorwrapper/layout/tiled.cpp +++ b/tests/cxx/unit_tests/tensorwrapper/layout/tiled.cpp @@ -53,10 +53,12 @@ TEST_CASE("Tiled") { SECTION("Value") { REQUIRE(matrix.has_shape()); + REQUIRE(matrix.shape().are_equal(matrix_shape)); REQUIRE(matrix.symmetry() == no_symm); REQUIRE(matrix.sparsity() == no_sparsity); REQUIRE(symm_matrix.has_shape()); + REQUIRE(symm_matrix.shape().are_equal(matrix_shape)); REQUIRE(symm_matrix.symmetry() == symm); REQUIRE(symm_matrix.sparsity() == no_sparsity); } @@ -68,6 +70,12 @@ TEST_CASE("Tiled") { REQUIRE(symm_matrix.has_shape()); } + SECTION("shape") { + REQUIRE_THROWS_AS(defaulted.shape(), std::runtime_error); + REQUIRE(matrix.shape().are_equal(matrix_shape)); + REQUIRE(symm_matrix.shape().are_equal(matrix_shape)); + } + SECTION("symmetry") { REQUIRE(defaulted.symmetry() == no_symm); REQUIRE(matrix.symmetry() == no_symm); From f71f7566e7b0c20c5333c3366188fa4a0965074d Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 15 Jul 2024 14:55:34 -0500 Subject: [PATCH 2/3] r2g --- .../allocator/allocator_base.hpp | 76 ++++++++++- include/tensorwrapper/allocator/eigen.hpp | 128 +++++++++++++++++- include/tensorwrapper/shape/smooth.hpp | 16 +++ src/tensorwrapper/allocator/eigen.cpp | 16 ++- .../tensorwrapper/allocator/eigen.cpp | 81 ++++++++++- .../unit_tests/tensorwrapper/shape/smooth.cpp | 16 +++ 6 files changed, 327 insertions(+), 6 deletions(-) diff --git a/include/tensorwrapper/allocator/allocator_base.hpp b/include/tensorwrapper/allocator/allocator_base.hpp index 854649e4..aade1915 100644 --- a/include/tensorwrapper/allocator/allocator_base.hpp +++ b/include/tensorwrapper/allocator/allocator_base.hpp @@ -45,29 +45,90 @@ class AllocatorBase : public detail_::PolymorphicBase { // -- Ctors and assignment // ------------------------------------------------------------------------- - explicit AllocatorBase(runtime_view_type rv) : m_rv_(std::move(rv)) {} - + /** @brief Polymorphically allocates a new buffer. + * + * This method type-erases the process of creating a buffer by dispatching + * to the derived class. In general the buffer created by this method will + * NOT be initialized, though this will depend on the default behavior of + * the backend. Use `construct` instead of `allocate` if you additionally + * want to guarantee initialization. + * + * Derived classes implement this method by overriding allocate_. + * + * @param[in] playout A pointer to the layout for the new buffer. + * + * @return The newly allocated, but not necessarily initialized buffer. + */ buffer_base_pointer allocate(layout_pointer playout) { return allocate_(std::move(playout)); } + /** @brief The runtime *this uses for allocating. + * + * Allocators are tied to runtimes. This method can be used to retrieve + * the runtime *this is using for allocation. + * + * @return A mutable reference to the runtime *this is using for allocating + * buffers. + * + * @throw None No throw guarantee. + */ runtime_view_reference runtime() noexcept { return m_rv_; } + /** @brief The runtime *this uses for allocating. + * + * This method is the same as the non-const version except that it returns + * the runtime in a read-only manner. + * + * @return A read-only reference to the runtime *this uses for allocating + * buffers. + * + * @throw None No throw guarantee. + */ const_runtime_view_reference runtime() const noexcept { return m_rv_; } // ------------------------------------------------------------------------- // -- Utility methods // ------------------------------------------------------------------------- + /** @brief Is *this value equal to @p rhs? + * + * This method is non-polymorphic and only compares the AllocatorBase part + * of *this to the AllocatorBase part of @p rhs. Two AllocatorBase objects + * are value equal if they contain views of the same runtime. + * + * @return True if *this is value equal to @p rhs and false otherwise. + * + * @throw None No throw guarantee. + */ bool operator==(const AllocatorBase& rhs) const noexcept { return m_rv_ == rhs.m_rv_; } + /** @brief Is *this different from @p rhs? + * + * This method defines "different" as "not value equal." See the + * documentation for operator== for the definition of value equal. + * + * @param[in] rhs The allocator to compare against. + * + * @return False if *this is value equal to @p rhs and true otherwise. + * + * @throw None No throw guarantee. + * + */ bool operator!=(const AllocatorBase& rhs) const noexcept { return !((*this) == rhs); } protected: + /** @brief Creates an allocator for the runtime @p rv. + * + * @param[in] rv The runtime in which to allocate buffers. + * + * @throw None No throw guarantee. + */ + explicit AllocatorBase(runtime_view_type rv) : m_rv_(std::move(rv)) {} /** @brief Creates *this so that it uses the same runtime as @p other. * * @param[in] other The allocator to make a copy of. @@ -77,6 +138,17 @@ class AllocatorBase : public detail_::PolymorphicBase { */ AllocatorBase(const AllocatorBase& other) = default; + /** @brief Derived classes should overwrite in order to implement allocate. + * + * Derived classes are charged with ensuring @p playout is a valid layout + * and then creating a buffer adhering to the layout. + * + * @param[in] playout The layout for the buffer to allocate. + * + * @throw std::bad_alloc if the allocation fails. Strong throw guarantee. + * @throw std::runtime_error if @p playout is not a valid layout. Strong + * throw guarantee. + */ virtual buffer_base_pointer allocate_(layout_pointer playout) = 0; private: diff --git a/include/tensorwrapper/allocator/eigen.hpp b/include/tensorwrapper/allocator/eigen.hpp index ea2a8998..4ed1004e 100644 --- a/include/tensorwrapper/allocator/eigen.hpp +++ b/include/tensorwrapper/allocator/eigen.hpp @@ -7,7 +7,12 @@ namespace tensorwrapper::allocator { /** @brief Used to allocate buffers which rely on Eigen tensors. * - * @tparam FloatType + * @tparam FloatType The numerical type the buffer will use to store the + * elements. + * @tparam Rank The rank of the tensor stored in the buffer. + * + * This allocator is capable of creating buffers with Eigen tensors in them. + * */ template class Eigen : public Replicated { @@ -24,6 +29,7 @@ class Eigen : public Replicated { using my_base_type::buffer_base_pointer; using my_base_type::const_base_reference; using my_base_type::layout_pointer; + using my_base_type::runtime_view_type; /// Type of a buffer containing an Eigen tensor using eigen_buffer_type = buffer::Eigen; @@ -43,19 +49,139 @@ class Eigen : public Replicated { // Reuse base class's ctors using my_base_type::my_base_type; + // ------------------------------------------------------------------------- + // -- Ctor + // ------------------------------------------------------------------------- + + /** @brief Creates a new Eigen allocator tied to the runtime @p rv. + * + * This ctor simply dispatches to the base class's ctor with the same + * signature. See the base class's description for more detail. + * + * @param[in] rv The runtime to use for allocating. + * + * @throw None No throw guarantee. + */ + explicit Eigen(runtime_view_type rv) : my_base_type(std::move(rv)) {} + + /** @brief Copies @p layout and dispatches to other overload. + * + * The buffer resulting from an allocator owns its layout. This method is + * a convenience function for when the layout for the buffer has not been + * allocated yet. + * + * @param[in] layout The to copy for the resulting buffer. + * + * @return An uninitialized buffer containing a copy of @p layout. + * + * @throw std::bad_alloc if there is a problem allocating the copy or the + * resulting buffer. Strong throw guarantee. + * @throw std::runtime_error if the provided layout is not compatible with + * *this. See the primary method for more + * details. + */ eigen_buffer_pointer allocate(const_eigen_layout_reference layout) { return allocate(std::make_unique(layout)); } + /** @brief Primary method for allocating Eigen-based buffers. + * + * This method simply checks that @p playout points to a layout compatible + * with the template parameters of *this and then creates a new Eigen + * tensor object. The elements of the Eigen tensor object are NOT + * initialized. + * + * @param[in] playout A pointer to the layout for the new buffer. + * + * @return An uninitialized buffer containing @p playout. + * + * @throw std::runtime_error if the provided layout does not have the same + * rank as @p Rank. Strong throw guarantee. + * @throw std::bad_alloc if there is a problem allocating the new buffer. + * Strong throw guarantee. + */ eigen_buffer_pointer allocate(eigen_layout_pointer playout); + /** @brief Allocates and initializes an Eigen buffer. + * + * @tparam LayoutType The type of @p layout. Must be a type such that + * `allocate(layout)` is a valid call. + * + * @param[in] layout The layout for the buffer. + * @param[in] value The value to initialize the tensor with. + * + * @return A buffer which is allocated and initialized. + * + * @throw std::runtime_error if the provided layout is not compatible with + * the template parameters of *this. Strong throw + * guarantee. + * @throw std::bad_alloc if there is a problem allocating the buffer. + * Strong throw guarantee. + */ + template + eigen_buffer_pointer construct(LayoutType&& layout, FloatType value) { + auto pbuffer = allocate(std::forward(layout)); + pbuffer->value().setConstant(value); + return pbuffer; + } + + /** @brief Is *this value equal to @p rhs? + * + * @tparam FloatType2 The numerical type @p rhs uses for its elements. + * @tparam Rank2 The rank of the tensors allocated by @p rhs. + * + * In addition to the definition of value equal stemming from the + * Replicated base class, two Eigen allocators are only value equal if they + * produce tensors with the same rank and numerical type. + * + * @param[in] rhs The allocator to compare to. + * + * @return True if *this is value equal to @p rhs and false otherwise. + * + * @throw None No throw guarantee. + */ + template + bool operator==(const Eigen& rhs) const noexcept { + if constexpr(!std::is_same_v || Rank != Rank2) { + return false; + } else { + return base_type::operator==(rhs); + } + } + + /** @brief Is this allocator different from @p rhs? + * + * @tparam FloatType2 The type @p rhs uses for floating-point elements. + * @tparam Rank2 The rank of @p rhs + * + * This method defines "different" as "not value equal." See the + * documentation for operator== for the definition of value equal. + * + * @param[in] rhs The allocator to compare against. + * + * @return False if *this is value equal to @p rhs and true otherwise. + * + * @throw None No throw guarantee. + */ + template + bool operator!=(const Eigen& rhs) const noexcept { + return !(*this == rhs); + } + protected: + /** @brief Polymorphic allocation of a new buffer. + * + * This method overrides the polymorphic allocation so that it creates a + * new Eigen buffer. + */ buffer_base_pointer allocate_(layout_pointer playout) override; + /// Implements clone by calling copy ctor base_pointer clone_() const override { return std::make_unique(*this); } + /// Implements are_equal, by deferring to the base's operator== bool are_equal_(const_base_reference rhs) const noexcept override { return my_base_type::are_equal_impl_(rhs); } diff --git a/include/tensorwrapper/shape/smooth.hpp b/include/tensorwrapper/shape/smooth.hpp index 45512440..fb4b8ad9 100644 --- a/include/tensorwrapper/shape/smooth.hpp +++ b/include/tensorwrapper/shape/smooth.hpp @@ -81,6 +81,22 @@ class Smooth : public ShapeBase { /// Defaulted no-throw dtor. ~Smooth() noexcept = default; + // ------------------------------------------------------------------------- + // -- Accessor methods + // ------------------------------------------------------------------------- + + /** @brief Returns the extent of the @p i -th mode. + * + * @param[in] i The mode the user wants the extent of. @p i must be in the + * range [0, rank()). + * + * @return The extent of the requested mode. + * + * @throw std::out_of_range if @p i is not in the range [0, range()). + * Strong throw guarantee. + */ + rank_type extent(size_type i) const { return m_extents_.at(i); } + // ------------------------------------------------------------------------- // -- Utility methods // ------------------------------------------------------------------------- diff --git a/src/tensorwrapper/allocator/eigen.cpp b/src/tensorwrapper/allocator/eigen.cpp index 9645a88f..f0ea92bc 100644 --- a/src/tensorwrapper/allocator/eigen.cpp +++ b/src/tensorwrapper/allocator/eigen.cpp @@ -1,7 +1,17 @@ #include #include +#include namespace tensorwrapper::allocator { +namespace { +template +auto unwrap_shape(const ShapeType& shape, std::index_sequence) { + // XXX: This is a hack until we have a general Shape API in place + auto const_shape = static_cast(shape); + return EigenTensorType(const_shape.extent(Is)...); +} + +} // namespace #define TPARAMS template #define EIGEN Eigen @@ -12,7 +22,11 @@ typename EIGEN::eigen_buffer_pointer EIGEN::allocate( using eigen_tensor_type = typename eigen_buffer_type::tensor_type; if(playout->shape().rank() != Rank) throw std::runtime_error("Rank of the layout is not compatible"); - return std::make_unique(eigen_tensor_type(), *playout); + + return std::make_unique( + unwrap_shape(playout->shape(), + std::make_index_sequence()), + *playout); } // ----------------------------------------------------------------------------- diff --git a/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp b/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp index 40fcfffd..c39d92ee 100644 --- a/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp +++ b/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp @@ -14,9 +14,11 @@ TEMPLATE_TEST_CASE("EigenAllocator", "", float, double) { using symmetry_type = typename layout_type::symmetry_type; using sparsity_type = typename layout_type::sparsity_type; using eigen_buffer_scalar = typename scalar_alloc_type::eigen_buffer_type; + using eigen_buffer_vector = typename vector_alloc_type::eigen_buffer_type; + using eigen_buffer_matrix = typename matrix_alloc_type::eigen_buffer_type; using eigen_scalar = typename eigen_buffer_scalar::tensor_type; - // using eigen_vector = typename vector_alloc_type::eigen_buffer_type; - // using eigen_matrix = typename matrix_alloc_type::eigen_buffer_type; + using eigen_vector = typename eigen_buffer_vector::tensor_type; + using eigen_matrix = typename eigen_buffer_matrix::tensor_type; parallelzone::runtime::RuntimeView rv; @@ -34,17 +36,92 @@ TEMPLATE_TEST_CASE("EigenAllocator", "", float, double) { scalar() = 0.0; eigen_buffer_scalar scalar_corr(scalar, scalar_layout); + eigen_vector vector(2); + vector.setConstant(1); + eigen_buffer_vector vector_corr(vector, vector_layout); + + eigen_matrix matrix(2, 2); + matrix.setConstant(2); + eigen_buffer_matrix matrix_corr(matrix, matrix_layout); + SECTION("Ctor") { SECTION("runtime") { REQUIRE(scalar_alloc.runtime() == rv); REQUIRE(vector_alloc.runtime() == rv); REQUIRE(matrix_alloc.runtime() == rv); } + + testing::test_copy_and_move_ctors(scalar_alloc, vector_alloc, + matrix_alloc); } SECTION("allocate(MonoTile)") { // N.b. allocate doesn't initialize tensor, so only compare layouts auto pscalar = scalar_alloc.allocate(scalar_layout); REQUIRE(pscalar->layout().are_equal(scalar_layout)); + + auto pvector = vector_alloc.allocate(vector_layout); + REQUIRE(pvector->layout().are_equal(vector_layout)); + + auto pmatrix = matrix_alloc.allocate(matrix_layout); + REQUIRE(pmatrix->layout().are_equal(matrix_layout)); + + // Throws if ranks don't match + using except_t = std::runtime_error; + REQUIRE_THROWS_AS(scalar_alloc.allocate(vector_layout), except_t); + } + + SECTION("allocate(std::unique_ptr)") { + // N.b. allocate doesn't initialize tensor, so only compare layouts + auto pscalar_layout = std::make_unique(scalar_layout); + auto pscalar = scalar_alloc.allocate(std::move(pscalar_layout)); + REQUIRE(pscalar->layout().are_equal(scalar_layout)); + + auto pvector_layout = std::make_unique(vector_layout); + auto pvector = vector_alloc.allocate(std::move(pvector_layout)); + REQUIRE(pvector->layout().are_equal(vector_layout)); + + auto pmatrix_layout = std::make_unique(matrix_layout); + auto pmatrix = matrix_alloc.allocate(std::move(pmatrix_layout)); + REQUIRE(pmatrix->layout().are_equal(matrix_layout)); + + // Throws if ranks don't match + using except_t = std::runtime_error; + auto pvector_layout2 = std::make_unique(vector_layout); + REQUIRE_THROWS_AS(scalar_alloc.allocate(std::move(pvector_layout2)), + except_t); + } + + SECTION("construct(value)") { + auto pscalar = scalar_alloc.construct(scalar_layout, 0); + REQUIRE(*pscalar == scalar_corr); + + auto pvector = vector_alloc.construct(vector_layout, 1); + REQUIRE(*pvector == vector_corr); + + auto pmatrix_layout = std::make_unique(matrix_layout); + auto pmatrix = matrix_alloc.construct(std::move(pmatrix_layout), 2); + REQUIRE(*pmatrix == matrix_corr); + + // Throws if ranks don't match + using except_t = std::runtime_error; + REQUIRE_THROWS_AS(scalar_alloc.allocate(vector_layout), except_t); + } + + SECTION("operator==") { + REQUIRE(scalar_alloc == scalar_alloc_type(rv)); + REQUIRE_FALSE(scalar_alloc == vector_alloc); + } + + SECTION("virtual_methods") { + SECTION("clone") { + auto pscalar = scalar_alloc.clone(); + REQUIRE(pscalar->are_equal(scalar_alloc)); + } + + SECTION("are_equal") { + REQUIRE(scalar_alloc.are_equal(scalar_alloc_type(rv))); + REQUIRE_FALSE(scalar_alloc.are_equal(vector_alloc)); + } } } diff --git a/tests/cxx/unit_tests/tensorwrapper/shape/smooth.cpp b/tests/cxx/unit_tests/tensorwrapper/shape/smooth.cpp index db2e33aa..b3f54ed9 100644 --- a/tests/cxx/unit_tests/tensorwrapper/shape/smooth.cpp +++ b/tests/cxx/unit_tests/tensorwrapper/shape/smooth.cpp @@ -57,6 +57,22 @@ TEST_CASE("Smooth") { test_copy_move_ctor_and_assignment(scalar, vector, matrix, tensor); } + SECTION("extent") { + REQUIRE_THROWS_AS(scalar.extent(0), std::out_of_range); + + REQUIRE(vector.extent(0) == 1); + REQUIRE_THROWS_AS(vector.extent(1), std::out_of_range); + + REQUIRE(matrix.extent(0) == matrix_extents[0]); + REQUIRE(matrix.extent(1) == matrix_extents[1]); + REQUIRE_THROWS_AS(matrix.extent(2), std::out_of_range); + + REQUIRE(tensor.extent(0) == 3); + REQUIRE(tensor.extent(1) == 4); + REQUIRE(tensor.extent(2) == 5); + REQUIRE_THROWS_AS(tensor.extent(3), std::out_of_range); + } + SECTION("Virtual implementations") { SECTION("clone") { REQUIRE(scalar.clone()->are_equal(scalar)); From 9fed33d9f45505a418f1d8ac628259cc1c519ce3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 15 Jul 2024 19:59:33 +0000 Subject: [PATCH 3/3] Committing clang-format changes --- .../tensorwrapper/allocator/allocator_base.hpp | 16 ++++++++++++++++ include/tensorwrapper/allocator/eigen.hpp | 16 ++++++++++++++++ include/tensorwrapper/allocator/local.hpp | 16 ++++++++++++++++ include/tensorwrapper/allocator/replicated.hpp | 16 ++++++++++++++++ .../detail_/unique_ptr_utilities.hpp | 16 ++++++++++++++++ src/tensorwrapper/allocator/eigen.cpp | 16 ++++++++++++++++ .../unit_tests/tensorwrapper/allocator/eigen.cpp | 16 ++++++++++++++++ .../detail_/unique_ptr_utilities.cpp | 16 ++++++++++++++++ 8 files changed, 128 insertions(+) diff --git a/include/tensorwrapper/allocator/allocator_base.hpp b/include/tensorwrapper/allocator/allocator_base.hpp index aade1915..d918716d 100644 --- a/include/tensorwrapper/allocator/allocator_base.hpp +++ b/include/tensorwrapper/allocator/allocator_base.hpp @@ -1,3 +1,19 @@ +/* + * Copyright 2024 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once #include #include diff --git a/include/tensorwrapper/allocator/eigen.hpp b/include/tensorwrapper/allocator/eigen.hpp index 4ed1004e..d6503df2 100644 --- a/include/tensorwrapper/allocator/eigen.hpp +++ b/include/tensorwrapper/allocator/eigen.hpp @@ -1,3 +1,19 @@ +/* + * Copyright 2024 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once #include #include diff --git a/include/tensorwrapper/allocator/local.hpp b/include/tensorwrapper/allocator/local.hpp index b07f1726..c9a82118 100644 --- a/include/tensorwrapper/allocator/local.hpp +++ b/include/tensorwrapper/allocator/local.hpp @@ -1,3 +1,19 @@ +/* + * Copyright 2024 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once #include diff --git a/include/tensorwrapper/allocator/replicated.hpp b/include/tensorwrapper/allocator/replicated.hpp index 8336b8bd..be537aae 100644 --- a/include/tensorwrapper/allocator/replicated.hpp +++ b/include/tensorwrapper/allocator/replicated.hpp @@ -1,3 +1,19 @@ +/* + * Copyright 2024 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once #include diff --git a/include/tensorwrapper/detail_/unique_ptr_utilities.hpp b/include/tensorwrapper/detail_/unique_ptr_utilities.hpp index 4b8a4fa6..2e18885e 100644 --- a/include/tensorwrapper/detail_/unique_ptr_utilities.hpp +++ b/include/tensorwrapper/detail_/unique_ptr_utilities.hpp @@ -1,3 +1,19 @@ +/* + * Copyright 2024 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once #include diff --git a/src/tensorwrapper/allocator/eigen.cpp b/src/tensorwrapper/allocator/eigen.cpp index f0ea92bc..96f7c035 100644 --- a/src/tensorwrapper/allocator/eigen.cpp +++ b/src/tensorwrapper/allocator/eigen.cpp @@ -1,3 +1,19 @@ +/* + * Copyright 2024 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include #include #include diff --git a/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp b/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp index c39d92ee..7924928f 100644 --- a/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp +++ b/tests/cxx/unit_tests/tensorwrapper/allocator/eigen.cpp @@ -1,3 +1,19 @@ +/* + * Copyright 2024 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include "../helpers.hpp" #include #include diff --git a/tests/cxx/unit_tests/tensorwrapper/detail_/unique_ptr_utilities.cpp b/tests/cxx/unit_tests/tensorwrapper/detail_/unique_ptr_utilities.cpp index 52ecd329..ed9030d3 100644 --- a/tests/cxx/unit_tests/tensorwrapper/detail_/unique_ptr_utilities.cpp +++ b/tests/cxx/unit_tests/tensorwrapper/detail_/unique_ptr_utilities.cpp @@ -1,3 +1,19 @@ +/* + * Copyright 2024 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include #include #include