diff --git a/include/tensorwrapper/buffer/eigen.hpp b/include/tensorwrapper/buffer/eigen.hpp index 12c00ba1..d41e384e 100644 --- a/include/tensorwrapper/buffer/eigen.hpp +++ b/include/tensorwrapper/buffer/eigen.hpp @@ -258,10 +258,46 @@ class Eigen : public Contiguous { pimpl_pointer m_pimpl_; }; +/** @brief Wraps downcasting a buffer to an Eigen buffer. + * + * @tparam FloatType The type of the elements in the resulting Buffer. + * + * This function is a convience function for using an allocator to convert + * @p b to a buffer::Eigen object. + * + * @param[in] b The BufferBase object to convert. + * + * @return A reference to @p b after downcasting it. + */ +template +Eigen& to_eigen_buffer(BufferBase& b); + +/** @brief Wraps downcasting a buffer to an Eigen buffer. + * + * @tparam FloatType The type of the elements in the resulting Buffer. + * + * This function is the same as the non-const overload except that result will + * be read-only. + * + * @param[in] b The BufferBase object to convert. + * + * @return A reference to @p b after downcasting it. + */ +template +const Eigen& to_eigen_buffer(const BufferBase& b); + #define DECLARE_EIGEN_BUFFER(TYPE) extern template class Eigen +#define DECLARE_TO_EIGEN_BUFFER(TYPE) \ + extern template Eigen& to_eigen_buffer(BufferBase&) +#define DECLARE_TO_CONST_EIGEN_BUFFER(TYPE) \ + extern template const Eigen& to_eigen_buffer(const BufferBase&) TW_APPLY_FLOATING_POINT_TYPES(DECLARE_EIGEN_BUFFER); +TW_APPLY_FLOATING_POINT_TYPES(DECLARE_TO_EIGEN_BUFFER); +TW_APPLY_FLOATING_POINT_TYPES(DECLARE_TO_CONST_EIGEN_BUFFER); #undef DECLARE_EIGEN_BUFFER +#undef DECLARE_TO_EIGEN_BUFFER +#undef DECLARE_TO_CONST_EIGEN_BUFFER } // namespace tensorwrapper::buffer diff --git a/include/tensorwrapper/utilities/to_json.hpp b/include/tensorwrapper/utilities/to_json.hpp new file mode 100644 index 00000000..537a214a --- /dev/null +++ b/include/tensorwrapper/utilities/to_json.hpp @@ -0,0 +1,43 @@ +/* + * 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 +#include +namespace tensorwrapper::utilities { + +/** @brief Adds a JSON representation of @p to @p os. + * + * This function can be used to print a tensor out in a JSON format. For dense + * tensors this will be as a list of lists such that number of nestings is + * equal to the rank of the tensor. + * + * @param[in,out] os The stream to print @p t to. After the function is called + * @p os will contain the JSON representation of @p t. + * @param[in] t The tensor to print to @p os. + * + * @note Since the caller controls the stream that is passed in, it is assumed + * that the caller has set the stream up to print floating point values + * in their desired format. For example, do + * `os << std::fixed << std::setprecision(8);` prior to calling `to_json` + * to guarantee all floating point values are printed with 8 decimal + * places. + * + * @return This function returns @p os by reference to support chaining. + */ +std::ostream& to_json(std::ostream& os, const Tensor& t); + +} // namespace tensorwrapper::utilities \ No newline at end of file diff --git a/include/tensorwrapper/utilities/utilities.hpp b/include/tensorwrapper/utilities/utilities.hpp index a5c3e1a5..415185ee 100644 --- a/include/tensorwrapper/utilities/utilities.hpp +++ b/include/tensorwrapper/utilities/utilities.hpp @@ -16,6 +16,7 @@ #pragma once #include +#include /// Namespace for helper functions namespace tensorwrapper::utilities {} \ No newline at end of file diff --git a/src/tensorwrapper/buffer/eigen.cpp b/src/tensorwrapper/buffer/eigen.cpp index b97a4fc6..057ef6e3 100644 --- a/src/tensorwrapper/buffer/eigen.cpp +++ b/src/tensorwrapper/buffer/eigen.cpp @@ -227,13 +227,33 @@ typename EIGEN::const_pimpl_reference EIGEN::pimpl_() const { return *m_pimpl_; } +TPARAMS +EIGEN& to_eigen_buffer(BufferBase& b) { + using allocator_type = allocator::Eigen; + return allocator_type::rebind(b); +} + +TPARAMS +const EIGEN& to_eigen_buffer(const BufferBase& b) { + using allocator_type = allocator::Eigen; + return allocator_type::rebind(b); +} + #undef EIGEN #undef TPARAMS #define DEFINE_EIGEN_BUFFER(TYPE) template class Eigen +#define DEFINE_TO_EIGEN_BUFFER(TYPE) \ + template Eigen& to_eigen_buffer(BufferBase&) +#define DEFINE_TO_CONST_EIGEN_BUFFER(TYPE) \ + template const Eigen& to_eigen_buffer(const BufferBase&) TW_APPLY_FLOATING_POINT_TYPES(DEFINE_EIGEN_BUFFER); +TW_APPLY_FLOATING_POINT_TYPES(DEFINE_TO_EIGEN_BUFFER); +TW_APPLY_FLOATING_POINT_TYPES(DEFINE_TO_CONST_EIGEN_BUFFER); #undef DEFINE_EIGEN_BUFFER +#undef DEFINE_TO_EIGEN_BUFFER +#undef DEFINE_TO_CONST_EIGEN_BUFFER } // namespace tensorwrapper::buffer \ No newline at end of file diff --git a/src/tensorwrapper/utilities/to_json.cpp b/src/tensorwrapper/utilities/to_json.cpp new file mode 100644 index 00000000..4d915f83 --- /dev/null +++ b/src/tensorwrapper/utilities/to_json.cpp @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#include +#include + +namespace tensorwrapper::utilities { + +using offset_type = std::size_t; +using offset_vector = std::vector; + +template +using buffer_type = buffer::Contiguous; + +template +void to_json_(std::ostream& os, const buffer_type& t, + offset_vector index) { + const auto& shape = t.layout().shape().as_smooth(); + auto rank = index.size(); + if(rank == t.rank()) { + os << t.get_elem(index); + return; + } else { + auto n_elements = shape.extent(rank); + index.push_back(0); + os << '['; + for(decltype(n_elements) i = 0; i < n_elements; ++i) { + index[rank] = i; + to_json_(os, t, index); + if(i + 1 < n_elements) os << ','; + } + os << ']'; + } +} + +std::ostream& to_json(std::ostream& os, const Tensor& t) { + offset_vector i; + const auto& buffer = buffer::to_eigen_buffer(t.buffer()); + to_json_(os, buffer, i); + return os; +} + +} // namespace tensorwrapper::utilities \ 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 33361aa8..0b30bcb0 100644 --- a/tests/cxx/unit_tests/tensorwrapper/buffer/eigen.cpp +++ b/tests/cxx/unit_tests/tensorwrapper/buffer/eigen.cpp @@ -239,3 +239,17 @@ TEMPLATE_LIST_TEST_CASE("Eigen", "", types::floating_point_types) { } } } + +TEMPLATE_LIST_TEST_CASE("to_eigen_buffer", "", types::floating_point_types) { + using buffer_type = buffer::Eigen; + + auto pscalar = testing::eigen_scalar(); + auto& eigen_scalar = static_cast(*pscalar); + eigen_scalar.set_elem({}, 10.0); + + buffer::BufferBase& scalar_base = eigen_scalar; + REQUIRE(&buffer::to_eigen_buffer(scalar_base) == &eigen_scalar); + + const buffer::BufferBase& cscalar_base = eigen_scalar; + REQUIRE(&buffer::to_eigen_buffer(cscalar_base) == &eigen_scalar); +} \ No newline at end of file diff --git a/tests/cxx/unit_tests/tensorwrapper/utilities/to_json.cpp b/tests/cxx/unit_tests/tensorwrapper/utilities/to_json.cpp new file mode 100644 index 00000000..90983d99 --- /dev/null +++ b/tests/cxx/unit_tests/tensorwrapper/utilities/to_json.cpp @@ -0,0 +1,57 @@ +/* + * 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 + +using namespace tensorwrapper; +using namespace testing; + +using tensorwrapper::utilities::to_json; + +TEMPLATE_LIST_TEST_CASE("to_json", "", std::tuple) { + Tensor scalar(smooth_scalar_()); + Tensor vector(smooth_vector_()); + Tensor matrix(smooth_matrix_()); + Tensor tensor(smooth_tensor3_()); + + std::stringstream ss; + + SECTION("scalar") { + auto pss = &(to_json(ss, scalar)); + REQUIRE(pss == &ss); + REQUIRE(ss.str() == "42"); + } + + SECTION("vector") { + auto pss = &(to_json(ss, vector)); + REQUIRE(pss == &ss); + REQUIRE(ss.str() == "[0,1,2,3,4]"); + } + + SECTION("matrix") { + auto pss = &(to_json(ss, matrix)); + REQUIRE(pss == &ss); + REQUIRE(ss.str() == "[[1,2],[3,4]]"); + } + + SECTION("tensor") { + auto pss = &(to_json(ss, tensor)); + REQUIRE(pss == &ss); + REQUIRE(ss.str() == "[[[1,2],[3,4]],[[5,6],[7,8]]]"); + } +} \ No newline at end of file