diff --git a/include/tensorwrapper/buffer/contiguous.hpp b/include/tensorwrapper/buffer/contiguous.hpp new file mode 100644 index 00000000..37094750 --- /dev/null +++ b/include/tensorwrapper/buffer/contiguous.hpp @@ -0,0 +1,74 @@ +/* + * Copyright 2025 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 + +namespace tensorwrapper::buffer { + +/** @brief Denotes that a buffer is held contiguously. + * + * Contiguous buffers are such that given a pointer to the first element `p`, + * the `i`-th element (`i` is zero based) is given by dereferencing the + * pointer `p + i`. Note that contiguous buffers are always vectors and storing + * higher rank tensors in a contiguous buffer requires "vectorization" of the + * tensor. In C++ vectorization is usually done in row-major format. + * + * @tparam FloatType the type of elements in the buffer. + */ +template +class Contiguous : public Replicated { +private: + /// Type *this derives from + using my_base_type = Replicated; + +public: + /// Type of each element + using element_type = FloatType; + + /// Type of a pointer to a mutable element_type object + using pointer = element_type*; + + /// Type of a pointer to a read-only element_type object + using const_pointer = const element_type*; + + /// Type used for offsets and indexing + using size_type = std::size_t; + + // Pull in base's ctors + using my_base_type::my_base_type; + + /// Returns the number of elements in contiguous memory + size_type size() const noexcept { return size_(); } + + /// Returns a mutable pointer to the first element in contiguous memory + pointer data() noexcept { return data_(); } + + /// Returns a read-only pointer to the first element in contiguous memory + const_pointer data() const noexcept { return data_(); } + +protected: + /// Derived class can override if it likes + virtual size_type size_() const noexcept { return layout().shape().size(); } + + /// Derived class should implement according to data() description + virtual pointer data_() noexcept = 0; + + /// Derived class should implement according to data() const description + virtual const_pointer data_() const noexcept = 0; +}; + +} // namespace tensorwrapper::buffer \ No newline at end of file diff --git a/include/tensorwrapper/buffer/eigen.hpp b/include/tensorwrapper/buffer/eigen.hpp index 94f68dd2..8446ead9 100644 --- a/include/tensorwrapper/buffer/eigen.hpp +++ b/include/tensorwrapper/buffer/eigen.hpp @@ -16,7 +16,7 @@ #pragma once #include -#include +#include #ifdef ENABLE_SIGMA #include @@ -31,19 +31,25 @@ namespace tensorwrapper::buffer { * */ template -class Eigen : public Replicated { +class Eigen : public Contiguous { private: /// Type of *this using my_type = Eigen; /// Type *this derives from - using my_base_type = Replicated; + using my_base_type = Contiguous; public: /// Pull in base class's types using typename my_base_type::buffer_base_pointer; using typename my_base_type::const_buffer_base_reference; + using typename my_base_type::const_labeled_reference; using typename my_base_type::const_layout_reference; + using typename my_base_type::const_pointer; + using typename my_base_type::dsl_reference; + using typename my_base_type::label_type; + using typename my_base_type::pointer; + using typename my_base_type::polymorphic_base; /// Type of a rank @p Rank tensor using floats of type @p FloatType using data_type = eigen::data_type; @@ -74,7 +80,7 @@ class Eigen : public Replicated { */ template Eigen(DataType&& t, const_layout_reference layout) : - Replicated(layout), m_tensor_(std::forward(t)) {} + my_base_type(layout), m_tensor_(std::forward(t)) {} /** @brief Initializes *this with a copy of @p other. * @@ -185,7 +191,7 @@ class Eigen : public Replicated { /// Implements are_equal by calling are_equal_impl_ bool are_equal_(const_buffer_base_reference rhs) const noexcept override { - return my_base_type::are_equal_impl_(rhs); + return my_base_type::template are_equal_impl_(rhs); } /// Implements addition_assignment by calling addition_assignment on state @@ -210,6 +216,10 @@ class Eigen : public Replicated { dsl_reference scalar_multiplication_(label_type this_labels, double scalar, const_labeled_reference rhs) override; + pointer data_() noexcept override { return m_tensor_.data(); } + + const_pointer data_() const noexcept override { return m_tensor_.data(); } + /// Implements to_string typename polymorphic_base::string_type to_string_() const override; diff --git a/src/tensorwrapper/buffer/eigen.cpp b/src/tensorwrapper/buffer/eigen.cpp index a0a35761..92ed1913 100644 --- a/src/tensorwrapper/buffer/eigen.cpp +++ b/src/tensorwrapper/buffer/eigen.cpp @@ -177,8 +177,7 @@ typename EIGEN::dsl_reference EIGEN::scalar_multiplication_( } TPARAMS -typename detail_::PolymorphicBase::string_type EIGEN::to_string_() - const { +typename EIGEN::polymorphic_base::string_type EIGEN::to_string_() const { std::stringstream ss; ss << m_tensor_; return ss.str(); diff --git a/tests/cxx/unit_tests/tensorwrapper/buffer/contiguous.cpp b/tests/cxx/unit_tests/tensorwrapper/buffer/contiguous.cpp new file mode 100644 index 00000000..6c74f741 --- /dev/null +++ b/tests/cxx/unit_tests/tensorwrapper/buffer/contiguous.cpp @@ -0,0 +1,66 @@ +/* + * 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 "../testing/testing.hpp" +#include +#include +#include + +using namespace tensorwrapper; +using namespace buffer; + +/* Testing strategy: + * + * - Contiguous is an abstract class. To test it we must create an instance of + * a derived class. We then will upcast to Contiguous and perform checks + * through the BufferBase interface. + + * + */ + +TEMPLATE_LIST_TEST_CASE("Contiguous", "", testing::floating_point_types) { + using base_type = Contiguous; + auto t0 = testing::eigen_scalar(); + auto t1 = testing::eigen_vector(); + + auto& base0 = static_cast(t0); + auto& base1 = static_cast(t1); + + SECTION("size") { + REQUIRE(base0.size() == 1); + REQUIRE(base1.size() == 5); + } + + SECTION("data()") { + REQUIRE(*base0.data() == TestType(42.0)); + + REQUIRE(*(base1.data() + 0) == TestType(0.0)); + REQUIRE(*(base1.data() + 1) == TestType(1.0)); + REQUIRE(*(base1.data() + 2) == TestType(2.0)); + REQUIRE(*(base1.data() + 3) == TestType(3.0)); + REQUIRE(*(base1.data() + 4) == TestType(4.0)); + } + + SECTION("data() const") { + REQUIRE(*std::as_const(base0).data() == TestType(42.0)); + + REQUIRE(*(std::as_const(base1).data() + 0) == TestType(0.0)); + REQUIRE(*(std::as_const(base1).data() + 1) == TestType(1.0)); + REQUIRE(*(std::as_const(base1).data() + 2) == TestType(2.0)); + REQUIRE(*(std::as_const(base1).data() + 3) == TestType(3.0)); + REQUIRE(*(std::as_const(base1).data() + 4) == TestType(4.0)); + } +} \ No newline at end of file diff --git a/tests/cxx/unit_tests/tensorwrapper/buffer/eigen.cpp b/tests/cxx/unit_tests/tensorwrapper/buffer/eigen.cpp index d11a2943..1e52cc3a 100644 --- a/tests/cxx/unit_tests/tensorwrapper/buffer/eigen.cpp +++ b/tests/cxx/unit_tests/tensorwrapper/buffer/eigen.cpp @@ -22,12 +22,6 @@ using namespace tensorwrapper; using namespace testing; -#ifdef ENABLE_SIGMA -using types2test = std::tuple; -#else -using types2test = std::tuple; -#endif - namespace { template @@ -47,7 +41,7 @@ void compare_eigen(const LHSType& lhs, const RHSType& rhs) { } // namespace -TEMPLATE_LIST_TEST_CASE("Eigen", "", types2test) { +TEMPLATE_LIST_TEST_CASE("Eigen", "", testing::floating_point_types) { using scalar_buffer = buffer::Eigen; using vector_buffer = buffer::Eigen; using matrix_buffer = buffer::Eigen; diff --git a/tests/cxx/unit_tests/tensorwrapper/testing/testing.hpp b/tests/cxx/unit_tests/tensorwrapper/testing/testing.hpp index cb1ddf7d..35bdee71 100644 --- a/tests/cxx/unit_tests/tensorwrapper/testing/testing.hpp +++ b/tests/cxx/unit_tests/tensorwrapper/testing/testing.hpp @@ -20,4 +20,15 @@ #include "helpers.hpp" #include "inputs.hpp" #include "layouts.hpp" -#include "shapes.hpp" \ No newline at end of file +#include "shapes.hpp" + +namespace tensorwrapper::testing { + +#ifdef ENABLE_SIGMA +using floating_point_types = + std::tuple; +#else +using floating_point_types = std::tuple; +#endif + +} // namespace tensorwrapper::testing \ No newline at end of file