diff --git a/INSTALL.md b/INSTALL.md index 17a3de08c0..1dcdfb3383 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -34,13 +34,13 @@ Both methods are supported. However, for most users we _strongly_ recommend to b - [CMake](https://cmake.org/), version 3.15 or higher; if CUDA support is needed, CMake 3.18 or higher is required. - [Git](https://git-scm.com/) 1.8 or later (required to obtain TiledArray and MADNESS source code from GitHub) -- [Eigen](http://eigen.tuxfamily.org/), version 3.3 or higher; if CUDA is enabled then 3.3.7 is required (will be downloaded automatically, if missing) +- [Eigen](http://eigen.tuxfamily.org/), version 3.3.5 or higher; if CUDA is enabled then 3.3.7 is required (will be downloaded automatically, if missing) - [Boost libraries](www.boost.org/), version 1.59 or higher (will be downloaded automatically, if missing). The following principal Boost components are used: - Boost.Iterator: header-only - Boost.Container: header-only - Boost.Test: header-only or (optionally) as a compiled library, *only used for unit testing* - Boost.Range: header-only, *only used for unit testing* -- [BTAS](https://github.com/ValeevGroup/BTAS), tag e540f265e1d16cc35b1b6726470160ed0046c530 . If usable BTAS installation is not found, TiledArray will download and compile +- [BTAS](https://github.com/ValeevGroup/BTAS), tag 5702259d5d207fe5a8e0c975c3cf1f610dcf381a . If usable BTAS installation is not found, TiledArray will download and compile BTAS from source. *This is the recommended way to compile BTAS for all users*. - [MADNESS](https://github.com/m-a-d-n-e-s-s/madness), tag b8069a25460b696d21cc8b739a91c9249da26575 . Only the MADworld runtime and BLAS/LAPACK C API component of MADNESS is used by TiledArray. diff --git a/external/eigen.cmake b/external/eigen.cmake index 438d256daf..8a465f6c64 100644 --- a/external/eigen.cmake +++ b/external/eigen.cmake @@ -49,11 +49,8 @@ if (TARGET TiledArray_Eigen) # INTERFACE libraries cannot be used as CMAKE_REQUIRED_LIBRARIES, so must manually transfer deps info get_property(EIGEN3_INCLUDE_DIRS TARGET TiledArray_Eigen PROPERTY INTERFACE_INCLUDE_DIRECTORIES) - if (NOT MADNESS_INTERNAL_INCLUDE_DIRS) - message(FATAL_ERROR "eigen.cmake must be loaded after calling detect_MADNESS_config()") - endif() list(APPEND CMAKE_REQUIRED_INCLUDES ${EIGEN3_INCLUDE_DIRS} ${PROJECT_BINARY_DIR}/src ${PROJECT_SOURCE_DIR}/src - ${MADNESS_INTERNAL_INCLUDE_DIRS} ${LAPACK_INCLUDE_DIRS}) + ${LAPACK_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LAPACK_LIBRARIES}) foreach(_def ${LAPACK_COMPILE_DEFINITIONS}) list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D${_def}") @@ -86,8 +83,11 @@ elseif(TA_EXPERT) else() - set(Eigen3_VERSION 3.3.7) - set(EIGEN3_URL_HASH MD5=b9e98a200d2455f06db9c661c5610496) + # last resort: unpack and copy to install dir + # N.B. NOT building via FetchContent since Eigen3 is not subprojectable due to polluting global namespace of targets (e.g. lapack, check, etc.) + + set(Eigen3_VERSION ${TA_INSTALL_EIGEN_VERSION}) + set(EIGEN3_URL_HASH ${TA_INSTALL_EIGEN_URL_HASH}) set(EIGEN3_URL https://gitlab.com/libeigen/eigen/-/archive/${Eigen3_VERSION}/eigen-${Eigen3_VERSION}.tar.bz2) include(ExternalProject) @@ -144,17 +144,5 @@ endif() # finish configuring TiledArray_Eigen and install if (TARGET TiledArray_Eigen) set(TiledArray_Eigen_VERSION "${Eigen3_VERSION}" CACHE STRING "Eigen3_VERSION of the library interfaced by TiledArray_Eigen target") - # TiledArray_Eigen uses LAPACK/MKL - # N.B. used to ... seems to be disabled -# target_link_libraries(TiledArray_Eigen INTERFACE ${LAPACK_LIBRARIES}) -# target_include_directories(TiledArray_Eigen INTERFACE ${LAPACK_INCLUDE_DIRS}) -# target_compile_definitions(TiledArray_Eigen INTERFACE ${LAPACK_COMPILE_DEFINITIONS}) -# target_compile_options(TiledArray_Eigen INTERFACE ${LAPACK_COMPILE_OPTIONS}) -# # Eigen's prototypes for BLAS interface libraries do not match MADNESS cblas -# if (MADNESS_HAS_MKL) -# # target_compile_definitions(TiledArray_Eigen INTERFACE EIGEN_USE_MKL EIGEN_USE_BLAS) -# else(MADNESS_HAS_MKL) -# # target_compile_definitions(TiledArray_Eigen INTERFACE EIGEN_USE_BLAS) -# endif(MADNESS_HAS_MKL) install(TARGETS TiledArray_Eigen EXPORT tiledarray COMPONENT tiledarray) endif(TARGET TiledArray_Eigen) diff --git a/external/versions.cmake b/external/versions.cmake index eddea3699d..338246146a 100644 --- a/external/versions.cmake +++ b/external/versions.cmake @@ -12,20 +12,20 @@ set(TA_INSTALL_BOOST_URL_HASH 882b48708d211a5f48e60b0124cf5863c1534cd544ecd0664b set(TA_INSTALL_BOOST_PREVIOUS_URL_HASH 882b48708d211a5f48e60b0124cf5863c1534cd544ecd0664bb534a4b5d506e9) # N.B. may need to update INSTALL.md manually with the CUDA-specific version -set(TA_TRACKED_EIGEN_VERSION 3.3) +set(TA_TRACKED_EIGEN_VERSION 3.3.5) set(TA_TRACKED_EIGEN_PREVIOUS_VERSION 3.3) -set(TA_INSTALL_EIGEN_VERSION 3.3.7) +set(TA_INSTALL_EIGEN_VERSION 3.4.0) set(TA_INSTALL_EIGEN_PREVIOUS_VERSION 3.3.7) -set(TA_INSTALL_EIGEN_URL_HASH b9e98a200d2455f06db9c661c5610496) -set(TA_INSTALL_EIGEN_PREVIOUS_URL_HASH b9e98a200d2455f06db9c661c5610496) +set(TA_INSTALL_EIGEN_URL_HASH SHA256=b4c198460eba6f28d34894e3a5710998818515104d6e74e5cc331ce31e46e626) +set(TA_INSTALL_EIGEN_PREVIOUS_URL_HASH MD5=b9e98a200d2455f06db9c661c5610496) set(TA_TRACKED_MADNESS_TAG b8069a25460b696d21cc8b739a91c9249da26575) set(TA_TRACKED_MADNESS_PREVIOUS_TAG f9aa38e4f46c5ea6ca6bbceb945beae5230f9ad0) set(TA_TRACKED_MADNESS_VERSION 0.10.1) set(TA_TRACKED_MADNESS_PREVIOUS_VERSION 0.10.1) -set(TA_TRACKED_BTAS_TAG e540f265e1d16cc35b1b6726470160ed0046c530) -set(TA_TRACKED_BTAS_PREVIOUS_TAG 6db4794185166fae4fc235f05b05e863a82a9dec) +set(TA_TRACKED_BTAS_TAG 5702259d5d207fe5a8e0c975c3cf1f610dcf381a) +set(TA_TRACKED_BTAS_PREVIOUS_TAG e540f265e1d16cc35b1b6726470160ed0046c530) set(TA_TRACKED_CUTT_TAG 0e8685bf82910bc7435835f846e88f1b39f47f09) set(TA_TRACKED_CUTT_PREVIOUS_TAG 592198b93c93b7ca79e7900b9a9f2e79f9dafec3) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 46ff408fa3..690b35979d 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -12,22 +12,10 @@ project(python-tiledarray) set(CMAKE_CXX_STANDARD 17) add_compile_options(-Wall) -find_package(Eigen3 3.3 REQUIRED) - -# find_package(MPI REQUIRED) -# include_directories(${MPI_INCLUDE_PATH}) - -find_package(Boost REQUIRED) # spirit absent from cmake -# include_directories(${BOOST_INCLUDE_DIRS}) - -#find_package (Python COMPONENTS Interpreter Development) -#include_directories(${PROJECT_SOURCE_DIR}/pybind11/include) -#python_add_library(python-tiledarray MODULE src/tiledarray.cc WITH_SOABI) - -#add_subdirectory(pybind11) pybind11_add_module(python-tiledarray MODULE src/tiledarray.cc) # mpi c libs need come from tiledarray +# Eigen and Boost also transitively come from tiledarray target_link_libraries(python-tiledarray PRIVATE tiledarray) # pybind uses sized deallocations when using C++17, clang requires -fsized-deallocation compiler flag to enable them @@ -43,14 +31,6 @@ set_target_properties( OUTPUT_NAME tiledarray ) -if (Eigen3::Eigen) - target_link_libraries(python-tiledarray INTERFACE Eigen3::Eigen) -else() - include_directories(${EIGEN3_INCLUDE_DIR}) -endif() - -target_link_libraries(python-tiledarray INTERFACE Boost::boost) - # tests if (BUILD_TESTING) # build step diff --git a/src/TiledArray/conversions/btas.h b/src/TiledArray/conversions/btas.h index 329c0b292b..28e5790e8f 100644 --- a/src/TiledArray/conversions/btas.h +++ b/src/TiledArray/conversions/btas.h @@ -115,7 +115,7 @@ namespace detail { /// TiledArray::DistArray /// \tparam DistArray_ a TiledArray::DistArray type -/// \tparam TArgs the type pack in btas::Tensor type +/// \tparam BTAS_Tensor_ a btas::Tensor type /// \param src The btas::Tensor object whose block will be copied /// \param dst The array that will hold the result /// \param i The index of the tile to be copied @@ -124,7 +124,7 @@ namespace detail { /// counter. template void counted_btas_subtensor_to_tensor(const BTAS_Tensor_* src, DistArray_* dst, - const typename DistArray_::ordinal_type i, + const typename Range::index_type i, madness::AtomicInt* counter) { typename DistArray_::value_type tensor(dst->trange().make_tile_range(i)); btas_subtensor_to_tensor(*src, tensor); @@ -134,8 +134,8 @@ void counted_btas_subtensor_to_tensor(const BTAS_Tensor_* src, DistArray_* dst, /// Task function for assigning a tensor to an Eigen submatrix -/// \tparam Tensor_ a TiledArray::Tensor type -/// \tparam TArgs the type pack in btas::Tensor type +/// \tparam TA_Tensor_ a TiledArray::Tensor type +/// \tparam BTAS_Tensor_ a btas::Tensor type /// \param src The source tensor /// \param dst The destination tensor /// \param counter The task counter @@ -171,10 +171,14 @@ inline auto make_shape(World&, const TiledArray::TiledRange&) { /// has sparse policy, a sparse map with large norm is created to ensure all the /// values from \c src copy to the \c DistArray_ object. The copy operation is /// done in parallel, and this function will block until all elements of -/// \c src have been copied into the result array tiles. The size of -/// \c world.size() must be equal to 1 or \c replicate must be equal to -/// \c true . If \c replicate is \c true, it is your responsibility to ensure -/// that the data in \c src is identical on all nodes. Upon completion, +/// \c src have been copied into the result array tiles. +/// Each tile is created +/// using the local contents of \c src, hence +/// it is your responsibility to ensure that the data in \c src +/// is distributed correctly among the ranks. If in doubt, you should replicate +/// \c src among the ranks prior to calling this. +/// +/// Upon completion, /// if the \c DistArray_ object has sparse policy truncate() is called.\n /// Usage: /// \code @@ -201,18 +205,20 @@ inline auto make_shape(World&, const TiledArray::TiledRange&) { /// \param[in] trange The tiled range of the new array /// \param[in] src The btas::Tensor object whose contents will be /// copied to the result. -/// \param[in] replicated \c true indicates that the -/// result array should be a -/// replicated array [default = false]. +/// \param replicated if true, the result will be replicated +/// [default = false]. +/// \param pmap the process map object [default=null]; initialized to the +/// default if \p replicated is false, or a replicated pmap if \p replicated +/// is true; ignored if \p replicated is true and \c world.size()>1 /// \return A \c DistArray_ object that is a copy of \c src /// \throw TiledArray::Exception When world size is greater than 1 /// \note If using 2 or more World ranks, set \c replicated=true and make sure /// \c matrix is the same on each rank! template -DistArray_ btas_tensor_to_array(World& world, - const TiledArray::TiledRange& trange, - const btas::Tensor& src, - bool replicated = false) { +DistArray_ btas_tensor_to_array( + World& world, const TiledArray::TiledRange& trange, + const btas::Tensor& src, bool replicated = false, + std::shared_ptr pmap = {}) { // Test preconditions const auto rank = trange.tiles_range().rank(); TA_ASSERT(rank == src.range().rank() && @@ -229,35 +235,26 @@ DistArray_ btas_tensor_to_array(World& world, using Policy_ = typename DistArray_::policy_type; const auto is_sparse = !is_dense_v; - // Check that this is not a distributed computing environment - if (!replicated) - TA_ASSERT( - world.size() == 1 && - "An array can be created from a btas::Tensor if the number of World " - "ranks is greater than 1 only when replicated=true."); - // Make a shape, only used if making a sparse array using Shape_ = typename DistArray_::shape_type; Shape_ shape = detail::make_shape(world, trange); // Create a new tensor - DistArray_ array = - (replicated && (world.size() > 1) - ? DistArray_( - world, trange, shape, - std::static_pointer_cast( - std::make_shared( - world, trange.tiles_range().volume()))) - : DistArray_(world, trange, shape)); + if (replicated && (world.size() > 1)) + pmap = std::static_pointer_cast( + std::make_shared( + world, trange.tiles_range().volume())); + DistArray_ array = (pmap ? DistArray_(world, trange, shape, pmap) + : DistArray_(world, trange, shape)); // Spawn copy tasks madness::AtomicInt counter; counter = 0; std::int64_t n = 0; - for (typename DistArray_::ordinal_type i = 0; i < array.size(); ++i) { + for (auto&& acc : array) { world.taskq.add( &detail::counted_btas_subtensor_to_tensor, &src, - &array, i, &counter); + &array, acc.index(), &counter); ++n; } @@ -276,7 +273,9 @@ DistArray_ btas_tensor_to_array(World& world, /// object. The copy operation is done in parallel, and this function will block /// until all elements of \c src have been copied into the result array tiles. /// The size of \c src.world().size() must be equal to 1 or \c src must be a -/// replicated TiledArray::DistArray. Usage: \code TiledArray::TArrayD +/// replicated TiledArray::DistArray. Usage: +/// \code +/// TiledArray::TArrayD /// array(world, trange); /// // Set tiles of array ... /// @@ -284,10 +283,14 @@ DistArray_ btas_tensor_to_array(World& world, /// \endcode /// \tparam Tile the tile type of \c src /// \tparam Policy the policy type of \c src +/// \tparam Range_ the range type of the result (either, btas::RangeNd or +/// TiledArray::Range) +/// \tparam Storage_ the storage type of the result /// \param[in] src The TiledArray::DistArray object whose contents -/// will be copied to the result. \return A \c btas::Tensor object that is a -/// copy of \c src \throw TiledArray::Exception When world size is greater than -/// 1 and \c src is not replicated +/// will be copied to the result. +/// \return A \c btas::Tensor object that is a copy of \c src +/// \throw TiledArray::Exception When world size is greater than +/// 1 and \c src is not replicated /// \param[in] target_rank the rank on which to create the BTAS tensor /// containing the data of \c src ; if \c target_rank=-1 then /// create the BTAS tensor on every rank (this requires @@ -295,13 +298,13 @@ DistArray_ btas_tensor_to_array(World& world, /// \return BTAS tensor object containing the data of \c src , if my rank equals /// \c target_rank or \c target_rank==-1 , /// default-initialized BTAS tensor otherwise. -template > -btas::Tensor -array_to_btas_tensor(const TiledArray::DistArray& src, - int target_rank = -1) { +template > +btas::Tensor array_to_btas_tensor( + const TiledArray::DistArray& src, int target_rank = -1) { // Test preconditions - if (target_rank == -1 && !src.pmap()->is_replicated()) + if (target_rank == -1 && src.world().size() > 1 && + !src.pmap()->is_replicated()) TA_ASSERT( src.world().size() == 1 && "TiledArray::array_to_btas_tensor(): a non-replicated array can only " @@ -310,7 +313,7 @@ array_to_btas_tensor(const TiledArray::DistArray& src, using result_type = btas::Tensor::element_type, - btas::DEFAULT::range, Storage>; + Range_, Storage_>; using result_range_type = typename result_type::range_type; // Construct the result diff --git a/src/TiledArray/conversions/eigen.h b/src/TiledArray/conversions/eigen.h index f3b033ed2b..912801fec4 100644 --- a/src/TiledArray/conversions/eigen.h +++ b/src/TiledArray/conversions/eigen.h @@ -59,11 +59,12 @@ typedef Eigen::Matrix, 1, Eigen::Dynamic> EigenVectorXcf; typedef Eigen::Matrix EigenVectorXi; typedef Eigen::Matrix EigenVectorXl; -/// Construct a const Eigen::Map object for a given Tensor object +/// Construct a const 2-d Eigen::Map object for a given Tensor object -/// \tparam T A contiguous tensor type, e.g. TiledArray::Tensor ; namely, \c -/// TiledArray::detail::is_contiguous_tensor_v must be true \tparam Storage -/// the tensor layout, either Eigen::RowMajor (default) or Eigen::ColMajor +/// \tparam T A contiguous tensor type, e.g. TiledArray::Tensor ; namely, +/// \c TiledArray::detail::is_contiguous_tensor_v must be true +/// \tparam Storage the tensor layout, either Eigen::RowMajor (default) or +/// Eigen::ColMajor /// \param tensor The tensor object, laid out according to Storage /// \param m The number of rows in the result matrix /// \param n The number of columns in the result matrix @@ -82,11 +83,12 @@ eigen_map(const T& tensor, const std::size_t m, const std::size_t n) { Eigen::AutoAlign>(tensor.data(), m, n); } -/// Construct an Eigen::Map object for a given Tensor object +/// Construct a 2-d Eigen::Map object for a given Tensor object -/// \tparam T A contiguous tensor type, e.g. TiledArray::Tensor ; namely, \c -/// TiledArray::detail::is_contiguous_tensor_v must be true \tparam Storage -/// the tensor layout, either Eigen::RowMajor (default) or Eigen::ColMajor +/// \tparam T A contiguous tensor type, e.g. TiledArray::Tensor ; namely, +/// \c TiledArray::detail::is_contiguous_tensor_v must be true +/// \tparam Storage the tensor layout, either Eigen::RowMajor (default) or +/// Eigen::ColMajor /// \param tensor The tensor object, laid out according to Storage /// \param m The number of rows in the result matrix /// \param n The number of columns in the result matrix @@ -105,13 +107,14 @@ eigen_map(T& tensor, const std::size_t m, const std::size_t n) { Eigen::AutoAlign>(tensor.data(), m, n); } -/// Construct a const Eigen::Map object for a given Tensor object +/// Construct a const 1-d Eigen::Map object for a given Tensor object -/// \tparam T A contiguous tensor type, e.g. TiledArray::Tensor ; namely, \c -/// TiledArray::detail::is_contiguous_tensor_v must be true \param tensor The -/// tensor object \param n The number of elements in the result matrix \return -/// An n element Eigen vector map for \c tensor \throw TiledArray::Exception -/// When n is not equal to \c tensor size +/// \tparam T A contiguous tensor type, e.g. TiledArray::Tensor ; namely, +/// \c TiledArray::detail::is_contiguous_tensor_v must be true +/// \param tensor The tensor object +/// \param n The number of elements in the result matrix +/// \return An n element Eigen vector map for \c tensor +/// \throw TiledArray::Exception When n is not equal to \c tensor size template >* = nullptr> inline Eigen::Map< @@ -125,7 +128,7 @@ eigen_map(const T& tensor, const std::size_t n) { Eigen::AutoAlign>(tensor.data(), n); } -/// Construct an Eigen::Map object for a given Tensor object +/// Construct a 1-d Eigen::Map object for a given Tensor object /// \tparam T A tensor type, e.g. TiledArray::Tensor /// \param tensor The tensor object @@ -143,12 +146,13 @@ eigen_map(T& tensor, const std::size_t n) { Eigen::AutoAlign>(tensor.data(), n); } -/// Construct a const Eigen::Map object for a given Tensor object +/// Construct a const 2-d Eigen::Map object for a given Tensor object /// The dimensions of the result tensor are extracted from the tensor itself -/// \tparam T A contiguous tensor type, e.g. TiledArray::Tensor ; namely, \c -/// TiledArray::detail::is_contiguous_tensor_v must be true \tparam Storage -/// the tensor layout, either Eigen::RowMajor (default) or Eigen::ColMajor +/// \tparam T A contiguous tensor type, e.g. TiledArray::Tensor ; namely, +/// \c TiledArray::detail::is_contiguous_tensor_v must be true +/// \tparam Storage the tensor layout, either Eigen::RowMajor (default) or +/// Eigen::ColMajor /// \param tensor The tensor object, laid out according to Storage /// \return An Eigen matrix map for \c tensor /// \throw TiledArray::Exception When \c tensor dimensions are not equal to 2 @@ -167,7 +171,7 @@ eigen_map(const T& tensor) { (tensor.range().rank() == 2u ? tensor_extent[1] : 1ul)); } -/// Construct an Eigen::Map object for a given Tensor object +/// Construct a 2-d Eigen::Map object for a given Tensor object /// The dimensions of the result tensor are extracted from the tensor itself /// \tparam T A contiguous tensor type, e.g. TiledArray::Tensor ; namely, \c @@ -414,10 +418,9 @@ A eigen_to_array(World& world, const typename A::trange_type& trange, // Check that trange matches the dimensions of other const auto rank = trange.tiles_range().rank(); - TA_ASSERT(rank == 1 || - rank == 2 && - "TiledArray::eigen_to_array(): The number of dimensions in " - "trange must match that of the Eigen matrix."); + TA_ASSERT((rank == 1 || rank == 2) && + "TiledArray::eigen_to_array(): The number of dimensions in " + "trange must match that of the Eigen matrix."); if (rank == 2) { TA_ASSERT( @@ -502,10 +505,9 @@ array_to_eigen(const DistArray& array) { const auto rank = array.trange().tiles_range().rank(); // Check that the array will fit in a matrix or vector - TA_ASSERT((rank == 2u) || - (rank == 1u) && - "TiledArray::array_to_eigen(): The array dimensions must be " - "equal to 1 or 2."); + TA_ASSERT(((rank == 2u) || (rank == 1u)) && + "TiledArray::array_to_eigen(): The array dimensions must be " + "equal to 1 or 2."); // Check that this is not a distributed computing environment or that the // array is replicated @@ -681,6 +683,347 @@ inline A column_major_buffer_to_array( pmap); } +///////////////// Eigen::Tensor conversions //////////////////////////////////// + +// clang-format off +/// Copy a block of a Eigen::Tensor into a (row-major) TiledArray::Tensor + +/// A block of Eigen::Tensor \c src will be copied into TiledArray::Tensor \c +/// dst. The block dimensions will be determined by the dimensions of the range +/// of \c dst . +/// \tparam T The tensor element type +/// \tparam NumIndices_ The order of \p src +/// \tparam Options_ +/// \tparam IndexType_ +/// \tparam Tensor_ A tensor type (e.g., TiledArray::Tensor or btas::Tensor, +/// optionally wrapped into TiledArray::Tile) +/// \param[in] src The source object; its subblock defined by the {lower,upper} +/// bounds \c {dst.lobound(),dst.upbound()} will be copied to \c dst +/// \param[out] dst The object that will contain the contents of the +/// corresponding subblock of src +/// \throw TiledArray::Exception When the dimensions of \c src and \c dst do not +/// match. +// clang-format on +template +inline void eigen_subtensor_to_tensor( + const Eigen::Tensor& src, + Tensor_& dst) { + TA_ASSERT(dst.range().rank() == NumIndices_); + + auto to_array = [](const auto& seq) { + TA_ASSERT(seq.size() == NumIndices_); + std::array result; + std::copy(seq.begin(), seq.end(), result.begin()); + return result; + }; + + [[maybe_unused]] auto reverse_extent_indices = []() { + std::array result; + std::iota(result.rbegin(), result.rend(), 0); + return result; + }; + + const auto& dst_range = dst.range(); + auto src_block = + src.slice(to_array(dst_range.lobound()), to_array(dst_range.extent())); + auto dst_eigen_map = Eigen::TensorMap< + Eigen::Tensor>( + dst.data(), to_array(dst_range.extent())); + if constexpr (static_cast(std::decay_t::Layout) == + static_cast(Eigen::ColMajor)) + dst_eigen_map = src_block.swap_layout().shuffle(reverse_extent_indices()); + else + dst_eigen_map = src_block; +} + +// clang-format off +/// Copy a (row-major) TiledArray::Tensor into a block of a Eigen::Tensor + +/// TiledArray::Tensor \c src will be copied into a block of Eigen::Tensor +/// \c dst. The block dimensions will be determined by +/// the dimensions of the range of \c src . +/// \tparam Tensor_ A tensor type (e.g., TiledArray::Tensor or btas::Tensor, +/// optionally wrapped into TiledArray::Tile) +/// \tparam T The tensor element type +/// \tparam NumIndices_ The order of \p dst +/// \tparam Options_ +/// \tparam IndexType_ +/// \param[in] src The source object whose contents will be copied into +/// a subblock of \c dst +/// \param[out] dst The destination object; its subblock defined by the +/// {lower,upper} bounds \c {src.lobound(),src.upbound()} will be +/// overwritten with the content of \c src +/// \throw TiledArray::Exception When the dimensions +/// of \c src and \c dst do not match. +// clang-format on +template +inline void tensor_to_eigen_subtensor( + const Tensor_& src, + Eigen::Tensor& dst) { + TA_ASSERT(src.range().rank() == NumIndices_); + + auto to_array = [](const auto& seq) { + TA_ASSERT(seq.size() == NumIndices_); + std::array result; + std::copy(seq.begin(), seq.end(), result.begin()); + return result; + }; + + [[maybe_unused]] auto reverse_extent_indices = []() { + std::array result; + std::iota(result.rbegin(), result.rend(), 0); + return result; + }; + + const auto& src_range = src.range(); + auto dst_block = + dst.slice(to_array(src_range.lobound()), to_array(src_range.extent())); + auto src_eigen_map = Eigen::TensorMap< + Eigen::Tensor>( + src.data(), to_array(src_range.extent())); + if constexpr (static_cast(std::decay_t::Layout) == + static_cast(Eigen::ColMajor)) + dst_block = src_eigen_map.swap_layout().shuffle(reverse_extent_indices()); + else + dst_block = src_eigen_map; +} + +namespace detail { + +/// Task function for converting Eigen::Tensor subblock to a +/// TiledArray::DistArray + +/// \tparam DistArray_ a TiledArray::DistArray type +/// \tparam Eigen_Tensor_ an Eigen::Tensor type +/// \param src The btas::Tensor object whose block will be copied +/// \param dst The array that will hold the result +/// \param i The index of the tile to be copied +/// \param counter The task counter +/// \internal OK to use bare ptrs as args as long as the user blocks on the +/// counter. +template +void counted_eigen_subtensor_to_tensor(const Eigen_Tensor_* src, + DistArray_* dst, + const typename Range::index_type i, + madness::AtomicInt* counter) { + typename DistArray_::value_type tensor(dst->trange().make_tile_range(i)); + eigen_subtensor_to_tensor(*src, tensor); + dst->set(i, tensor); + (*counter)++; +} + +/// Task function for assigning a tensor to an Eigen subtensor + +/// \tparam Tensor_ a TiledArray::Tensor type +/// \tparam Eigen_Tensor_ an Eigen::Tensor type +/// \param src The source tensor +/// \param dst The destination tensor +/// \param counter The task counter +template +void counted_tensor_to_eigen_subtensor(const TA_Tensor_& src, + Eigen_Tensor_* dst, + madness::AtomicInt* counter) { + tensor_to_eigen_subtensor(src, *dst); + (*counter)++; +} + +template +auto make_ta_shape(World& world, const TiledArray::TiledRange& trange); + +template <> +inline auto make_ta_shape(World& world, + const TiledArray::TiledRange& trange) { + TiledArray::Tensor tile_norms(trange.tiles_range(), + std::numeric_limits::max()); + return TiledArray::SparseShape(world, tile_norms, trange); +} + +template <> +inline auto make_ta_shape(World&, const TiledArray::TiledRange&) { + return TiledArray::DenseShape{}; +} + +} // namespace detail + +/// Convert a Eigen::Tensor object into a TiledArray::DistArray object + +/// This function will copy the contents of \c src into a \c DistArray_ object +/// that is tiled according to the \c trange object. If the \c DistArray_ object +/// has sparse policy, a sparse map with large norm is created to ensure all the +/// values from \c src copy to the \c DistArray_ object. The copy operation is +/// done in parallel, and this function will block until all elements of +/// \c src have been copied into the result array tiles. +/// Each tile is created +/// using the local contents of \c src, hence +/// it is your responsibility to ensure that the data in \c src +/// is distributed correctly among the ranks. If in doubt, you should replicate +/// \c src among the ranks prior to calling this. +/// +/// Upon completion, +/// if the \c DistArray_ object has sparse policy truncate() is called.\n +/// Usage: +/// \code +/// Eigen::Tensor src(100, 100, 100); +/// // Fill src with data ... +/// +/// // Create a range for the new array object +/// std::vector blocks; +/// for(std::size_t i = 0ul; i <= 100ul; i += 10ul) +/// blocks.push_back(i); +/// std::array blocks3 = +/// {{ TiledArray::TiledRange1(blocks.begin(), blocks.end()), +/// TiledArray::TiledRange1(blocks.begin(), blocks.end()), +/// TiledArray::TiledRange1(blocks.begin(), blocks.end()) }}; +/// TiledArray::TiledRange trange(blocks3.begin(), blocks3.end()); +/// +/// // Create an Array from the source btas::Tensor object +/// TiledArray::TArrayD array = +/// eigen_tensor_to_array(world, trange, src); +/// \endcode +/// \tparam DistArray_ a TiledArray::DistArray type +/// \tparam NumIndices_ The order of \p dst +/// \tparam Options_ +/// \tparam IndexType_ +/// \param[in,out] world The world where the result array will live +/// \param[in] trange The tiled range of the new array +/// \param[in] src The Eigen::Tensor object whose contents will be +/// copied to the result. +/// \param replicated if true, the result will be replicated +/// [default = false]. +/// \param pmap the process map object [default=null]; initialized to the +/// default if \p replicated is false, or a replicated pmap if \p replicated +/// is true; ignored if \p replicated is true and \c world.size()>1 +/// \return A \c DistArray_ object that is a copy of \c src +/// \throw TiledArray::Exception When world size is greater than 1 +/// \note If using 2 or more World ranks, set \c replicated=true and make sure +/// \c matrix is the same on each rank! +template +DistArray_ eigen_tensor_to_array( + World& world, const TiledArray::TiledRange& trange, + const Eigen::Tensor& src, + bool replicated = false, + std::shared_ptr pmap = {}) { + // Test preconditions + const auto rank = trange.tiles_range().rank(); + TA_ASSERT(rank == NumIndices_ && + "TiledArray::eigen_tensor_to_array(): rank of destination " + "trange does not match the rank of source Eigen tensor."); + auto dst_range_extents = trange.elements_range().extent(); + for (std::remove_const_t d = 0; d != rank; ++d) { + TA_ASSERT(dst_range_extents[d] == src.dimension(d) && + "TiledArray::eigen_tensor_to_array(): source dimension does " + "not match destination dimension."); + } + + using Tensor_ = Eigen::Tensor; + using Policy_ = typename DistArray_::policy_type; + const auto is_sparse = !is_dense_v; + + // Make a shape, only used if making a sparse array + using Shape_ = typename DistArray_::shape_type; + Shape_ shape = detail::make_ta_shape(world, trange); + + // Create a new tensor + if (replicated && (world.size() > 1)) + pmap = std::static_pointer_cast( + std::make_shared( + world, trange.tiles_range().volume())); + DistArray_ array = (pmap ? DistArray_(world, trange, shape, pmap) + : DistArray_(world, trange, shape)); + + // Spawn copy tasks + madness::AtomicInt counter; + counter = 0; + std::int64_t n = 0; + for (auto&& acc : array) { + world.taskq.add( + &detail::counted_eigen_subtensor_to_tensor, &src, + &array, acc.index(), &counter); + ++n; + } + + // Wait until the write tasks are complete + array.world().await([&counter, n]() { return counter == n; }); + + // Analyze tiles norms and truncate based on sparse policy + if (is_sparse) truncate(array); + + return array; +} + +/// Convert a TiledArray::DistArray object into a Eigen::Tensor object + +/// This function will copy the contents of \c src into a \c Eigen::Tensor +/// object. The copy operation is done in parallel, and this function will block +/// until all elements of \c src have been copied into the result array tiles. +/// The size of \c src.world().size() must be equal to 1 or \c src must be a +/// replicated TiledArray::DistArray. Usage: +/// \code +/// TiledArray::TArrayD +/// array(world, trange); +/// // Set tiles of array ... +/// +/// auto t = array_to_eigen_tensor(array); +/// \endcode +/// \tparam Tile the tile type of \c src +/// \tparam Policy the policy type of \c src +/// \param[in] src The TiledArray::DistArray object whose contents +/// will be copied to the result. +/// \return A \c Eigen::Tensor object that is a copy of \c src +/// \throw TiledArray::Exception When world size is greater than +/// 1 and \c src is not replicated +/// \param[in] target_rank the rank on which to create the Eigen:Tensor +/// containing the data of \c src ; if \c target_rank=-1 then +/// create the Eigen::Tensor on every rank (this requires +/// that \c src.is_replicated()==true ) +/// \return Eigen::Tensor object containing the data of \c src , if my rank +/// equals +/// \c target_rank or \c target_rank==-1 , +/// default-initialized Eigen::Tensor otherwise. +template +Tensor array_to_eigen_tensor(const TiledArray::DistArray& src, + int target_rank = -1) { + // Test preconditions + if (target_rank == -1 && src.world().size() > 1 && + !src.pmap()->is_replicated()) + TA_ASSERT( + src.world().size() == 1 && + "TiledArray::array_to_eigen_tensor(): a non-replicated array can only " + "be converted to a Eigen::Tensor on every rank if the number of World " + "ranks is 1."); + + using result_type = Tensor; + + // Construct the result + if (target_rank == -1 || src.world().rank() == target_rank) { + // if array is sparse must initialize to zero + result_type result(src.trange().elements_range().extent()); + result.setZero(); + + // Spawn tasks to copy array tiles to btas::Tensor + madness::AtomicInt counter; + counter = 0; + int n = 0; + for (std::size_t i = 0; i < src.size(); ++i) { + if (!src.is_zero(i)) { + src.world().taskq.add( + &detail::counted_tensor_to_eigen_subtensor, + src.find(i), &result, &counter); + ++n; + } + } + + // Wait until the write tasks are complete + src.world().await([&counter, n]() { return counter == n; }); + + return result; + } else // else + return result_type{}; +} + } // namespace TiledArray #endif // TILEDARRAY_CONVERSIONS_EIGEN_H__INCLUDED diff --git a/src/TiledArray/external/btas.h b/src/TiledArray/external/btas.h index b990468d88..483be905df 100644 --- a/src/TiledArray/external/btas.h +++ b/src/TiledArray/external/btas.h @@ -109,18 +109,18 @@ inline bool is_congruent(const btas::RangeNd& r1, r2.extent_data()); } -template -decltype(auto) make_ti(const btas::Tensor& arg) { - return TiledArray::detail::TensorInterface< - const T, TiledArray::Range, btas::Tensor>( - arg.range(), arg.data()); +template +decltype(auto) make_ti(const btas::Tensor& arg) { + return TiledArray::detail::TensorInterface>( + TiledArray::detail::make_ta_range(arg.range()), arg.data()); } -template -decltype(auto) make_ti(btas::Tensor& arg) { - return TiledArray::detail::TensorInterface< - T, TiledArray::Range, btas::Tensor>( - arg.range(), arg.data()); +template +decltype(auto) make_ti(btas::Tensor& arg) { + return TiledArray::detail::TensorInterface>( + TiledArray::detail::make_ta_range(arg.range()), arg.data()); } template @@ -852,7 +852,9 @@ struct Cast, auto operator()(const btas::Tensor& arg) const { TiledArray::Tensor result(detail::make_ta_range(arg.range())); using std::begin; - std::copy(btas::cbegin(arg), btas::cend(arg), begin(result)); + using std::cbegin; + using std::cend; + std::copy(cbegin(arg), cend(arg), begin(result)); return result; } }; diff --git a/src/TiledArray/external/eigen.h b/src/TiledArray/external/eigen.h index df1f42b8f0..6ee0eaea3f 100644 --- a/src/TiledArray/external/eigen.h +++ b/src/TiledArray/external/eigen.h @@ -46,13 +46,15 @@ TILEDARRAY_PRAGMA_GCC(system_header) #endif #include +#include #if defined(EIGEN_USE_LAPACKE) || defined(EIGEN_USE_LAPACKE_STRICT) #if !EIGEN_VERSION_AT_LEAST(3, 3, 7) #error "Eigen3 < 3.3.7 with LAPACKE enabled may give wrong eigenvalue results" -#error "Either turn off EIGEN_USE_LAPACKE/EIGEN_USE_LAPACKE_STRICT or use Eigen3 3.3.7" +#error \ + "Either turn off EIGEN_USE_LAPACKE/EIGEN_USE_LAPACKE_STRICT or use Eigen3 3.3.7" #endif -#endif // EIGEN_USE_LAPACKE || EIGEN_USE_LAPACKE_STRICT +#endif // EIGEN_USE_LAPACKE || EIGEN_USE_LAPACKE_STRICT TILEDARRAY_PRAGMA_GCC(diagnostic pop) @@ -96,6 +98,38 @@ struct ArchiveLoadImpl< } }; +template +struct ArchiveStoreImpl< + Archive, Eigen::Tensor> { + static inline void store( + const Archive& ar, + const Eigen::Tensor& t) { + using idx_t = typename Eigen::Tensor::Index; + std::array extents; + for (int d = 0; d != NumIndices_; ++d) extents[d] = t.dimension(d); + ar& extents; + if (t.size()) ar& madness::archive::wrap(t.data(), t.size()); + } +}; + +template +struct ArchiveLoadImpl< + Archive, Eigen::Tensor> { + static inline void load( + const Archive& ar, + Eigen::Tensor& t) { + using idx_t = typename Eigen::Tensor::Index; + std::array extents; + ar& extents; + t.resize(extents); + if (t.size()) ar& madness::archive::wrap(t.data(), t.size()); + } +}; + } // namespace archive } // namespace madness diff --git a/src/TiledArray/sparse_shape.h b/src/TiledArray/sparse_shape.h index e36dedfe63..9a3175760e 100644 --- a/src/TiledArray/sparse_shape.h +++ b/src/TiledArray/sparse_shape.h @@ -731,7 +731,7 @@ class SparseShape { std::shared_ptr size_vectors( new vector_type[rank], std::default_delete()); - int d = 0; + unsigned int d = 0; using std::begin; using std::end; auto lower_it = begin(lower_bound); @@ -772,7 +772,7 @@ class SparseShape { std::shared_ptr size_vectors( new vector_type[rank], std::default_delete()); - int d = 0; + unsigned int d = 0; for (auto&& bound_d : bounds) { // Get the new range size const auto lower_d = detail::at(bound_d, 0); @@ -1566,8 +1566,8 @@ class SparseShape { } template >::type* = nullptr> + typename std::enable_if< + madness::is_input_archive_v>::type* = nullptr> void serialize(const Archive& ar) { ar& tile_norms_; const unsigned int dim = tile_norms_.range().rank(); diff --git a/tests/btas.cpp b/tests/btas.cpp index 12809b5b0b..a31329a80d 100644 --- a/tests/btas.cpp +++ b/tests/btas.cpp @@ -332,10 +332,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(dense_array_conversion, bTensor, tensor_types) { using TArray = TiledArray::TArray; TArray dst; const auto replicated = true; - if (GlobalFixture::world->size() > 1) - BOOST_REQUIRE_THROW(dst = btas_tensor_to_array( - *GlobalFixture::world, trange, src, not replicated), - TiledArray::Exception); + BOOST_REQUIRE_NO_THROW( + dst = btas_tensor_to_array(*GlobalFixture::world, trange, src, + not replicated)); BOOST_REQUIRE_NO_THROW(dst = btas_tensor_to_array( *GlobalFixture::world, trange, src, replicated)); @@ -393,10 +392,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(sparse_array_conversion, bTensor, tensor_types) { using TSpArray = TiledArray::TSpArray; TSpArray dst; const auto replicated = true; - if (GlobalFixture::world->size() > 1) - BOOST_REQUIRE_THROW(dst = btas_tensor_to_array( - *GlobalFixture::world, trange, src, not replicated), - TiledArray::Exception); + BOOST_REQUIRE_NO_THROW( + dst = btas_tensor_to_array(*GlobalFixture::world, trange, src, + not replicated)); BOOST_REQUIRE_NO_THROW(dst = btas_tensor_to_array( *GlobalFixture::world, trange, src, replicated)); diff --git a/tests/eigen.cpp b/tests/eigen.cpp index 07125da65a..6196591713 100644 --- a/tests/eigen.cpp +++ b/tests/eigen.cpp @@ -28,21 +28,29 @@ struct EigenFixture : public TiledRangeFixture { EigenFixture() : trange(dims.begin(), dims.begin() + 2), trange1(dims.begin(), dims.begin() + 1), + trangeN(dims.begin(), dims.begin() + GlobalFixture::dim), array(*GlobalFixture::world, trange), array1(*GlobalFixture::world, trange1), + arrayN(*GlobalFixture::world, trangeN), matrix(dims[0].elements_range().second, dims[1].elements_range().second), rmatrix(dims[0].elements_range().second, dims[1].elements_range().second), - vector(dims[0].elements_range().second) {} + vector(dims[0].elements_range().second), + tensor(extents), + rtensor(extents) {} TiledRange trange; TiledRange trange1; + TiledRange trangeN; TArrayI array; TArrayI array1; + TArrayI arrayN; Eigen::MatrixXi matrix; EigenMatrixXi rmatrix; Eigen::VectorXi vector; + Eigen::Tensor tensor; + Eigen::Tensor rtensor; }; BOOST_FIXTURE_TEST_SUITE(eigen_suite, EigenFixture) @@ -157,8 +165,7 @@ BOOST_AUTO_TEST_CASE(tensor_to_submatrix) { BOOST_AUTO_TEST_CASE(matrix_to_array) { // Fill the matrix with random data and replicate across the world matrix = decltype(matrix)::Random(matrix.rows(), matrix.cols()); - GlobalFixture::world->gop.broadcast(matrix.data(), - matrix.rows() * matrix.cols(), 0); + GlobalFixture::world->gop.broadcast_serializable(matrix, 0); // Copy matrix to array BOOST_CHECK_NO_THROW( @@ -179,7 +186,7 @@ BOOST_AUTO_TEST_CASE(matrix_to_array) { BOOST_AUTO_TEST_CASE(vector_to_array) { // Fill the vector with random data and replicate across the world vector = Eigen::VectorXi::Random(vector.size()); - GlobalFixture::world->gop.broadcast(vector.data(), vector.size(), 0); + GlobalFixture::world->gop.broadcast_serializable(vector, 0); // Convert the vector to an array BOOST_CHECK_NO_THROW((array1 = eigen_to_array(*GlobalFixture::world, @@ -365,4 +372,142 @@ BOOST_AUTO_TEST_CASE(array_to_vector) { } } +BOOST_AUTO_TEST_CASE(subtensor_to_tensor) { + // Fill the tensor with random data + tensor.setRandom(); + // Make a target tensor + Tensor ta_tensor(trangeN.make_tile_range(0)); + + // Copy the sub matrix to the tensor objects + BOOST_CHECK_NO_THROW(eigen_subtensor_to_tensor(tensor, ta_tensor)); + + // Check that the block contains the same values as the tensor + for (Range::const_iterator it = ta_tensor.range().begin(); + it != ta_tensor.range().end(); ++it) { + const auto& ta_idx = (*it); + std::array idx; + std::copy(ta_idx.begin(), ta_idx.end(), idx.begin()); + BOOST_CHECK_EQUAL(ta_tensor[*it], tensor(idx)); + } +} + +BOOST_AUTO_TEST_CASE(tensor_to_subtensor) { + // Fill a tensor with data + Tensor ta_tensor(trangeN.make_tile_range(0)); + GlobalFixture::world->srand(27); + for (auto it = ta_tensor.begin(); it != ta_tensor.end(); ++it) + *it = GlobalFixture::world->rand(); + + // Copy the tensor to the submatrix block + BOOST_CHECK_NO_THROW(tensor_to_eigen_subtensor(ta_tensor, tensor)); + + // Check that the block contains the same values as the tensor + for (auto it = ta_tensor.range().begin(); it != ta_tensor.range().end(); + ++it) { + const auto& ta_idx = (*it); + std::array idx; + std::copy(ta_idx.begin(), ta_idx.end(), idx.begin()); + BOOST_CHECK_EQUAL(tensor(idx), ta_tensor[*it]); + } +} + +BOOST_AUTO_TEST_CASE(tensor_to_array) { + // Fill tensor with random data and replicate across the world + tensor.setRandom(); + GlobalFixture::world->gop.broadcast_serializable(tensor, 0); + + // test serialization if have more than 1 rank + if (GlobalFixture::world->size() > 1) { + decltype(tensor) tensor_copy; + if (GlobalFixture::world->rank() == 1) tensor_copy = tensor; + GlobalFixture::world->gop.broadcast_serializable(tensor_copy, 1); + Eigen::Tensor eq = (tensor == tensor_copy).all(); + BOOST_CHECK(eq() == true); + } + + // Copy matrix to array + BOOST_CHECK_NO_THROW((array = eigen_tensor_to_array( + *GlobalFixture::world, trangeN, tensor))); + + // Check that the data in array is equal to that in matrix + for (Range::const_iterator it = array.range().begin(); + it != array.range().end(); ++it) { + Future tile = array.find(*it); + for (Range::const_iterator tile_it = tile.get().range().begin(); + tile_it != tile.get().range().end(); ++tile_it) { + std::array idx; + auto& t_idx = *tile_it; + std::copy(t_idx.begin(), t_idx.end(), idx.begin()); + BOOST_CHECK_EQUAL(tile.get()[*tile_it], tensor(idx)); + } + } +} + +BOOST_AUTO_TEST_CASE(array_to_tensor) { + using Tensor = Eigen::Tensor; + using RowTensor = Eigen::Tensor; + auto a_to_e_rowmajor = [](const TArrayI& array) -> RowTensor { + return array_to_eigen_tensor(array); + }; + + auto to_array = [](const auto& seq) { + TA_ASSERT(seq.size() == GlobalFixture::dim); + std::array result; + std::copy(seq.begin(), seq.end(), result.begin()); + return result; + }; + + // Fill local tiles with data + GlobalFixture::world->srand(27); + TArrayI::pmap_interface::const_iterator it = arrayN.pmap()->begin(); + TArrayI::pmap_interface::const_iterator end = arrayN.pmap()->end(); + for (; it != end; ++it) { + TArrayI::value_type tile(arrayN.trange().make_tile_range(*it)); + for (TArrayI::value_type::iterator tile_it = tile.begin(); + tile_it != tile.end(); ++tile_it) { + *tile_it = GlobalFixture::world->rand(); + } + arrayN.set(*it, tile); + } + + if (GlobalFixture::world->size() > 1) { + // Check that array_to_eigen_tensor throws when there is more than one node + BOOST_CHECK_THROW(array_to_eigen_tensor(arrayN), + TiledArray::Exception); + } + + // Distribute the data of arrayN to all nodes + if (GlobalFixture::world->size() > 1) { + arrayN.make_replicated(); + BOOST_CHECK(arrayN.pmap()->is_replicated()); + } + + // Convert the array to an Eigen matrix + BOOST_CHECK_NO_THROW(tensor = array_to_eigen_tensor(arrayN)); + BOOST_CHECK_NO_THROW(rtensor = a_to_e_rowmajor(arrayN)); + + // Check that the matrix dimensions are the same as the array + BOOST_CHECK_EQUAL_COLLECTIONS( + tensor.dimensions().begin(), tensor.dimensions().end(), + arrayN.trange().elements_range().extent().begin(), + arrayN.trange().elements_range().extent().end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + rtensor.dimensions().begin(), rtensor.dimensions().end(), + arrayN.trange().elements_range().extent().begin(), + arrayN.trange().elements_range().extent().end()); + + // Check that the data in vector matches the data in array + for (Range::const_iterator it = arrayN.range().begin(); + it != arrayN.range().end(); ++it) { + BOOST_CHECK(arrayN.is_local(*it)); + + Future tile = arrayN.find(*it); + for (Range::const_iterator tile_it = tile.get().range().begin(); + tile_it != tile.get().range().end(); ++tile_it) { + BOOST_CHECK_EQUAL(tensor(to_array(*tile_it)), tile.get()[*tile_it]); + BOOST_CHECK_EQUAL(rtensor(to_array(*tile_it)), tile.get()[*tile_it]); + } + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/global_fixture.h b/tests/global_fixture.h index 73260e5f3c..d0f8b4caac 100644 --- a/tests/global_fixture.h +++ b/tests/global_fixture.h @@ -61,7 +61,7 @@ struct GlobalFixture { GlobalFixture(); ~GlobalFixture(); - static const unsigned int dim = TEST_DIM; + static constexpr unsigned int dim = TEST_DIM; static madness::World* world; static const std::array primes; diff --git a/tests/linalg.cpp b/tests/linalg.cpp index 2f9d54b5bf..c5a706c565 100644 --- a/tests/linalg.cpp +++ b/tests/linalg.cpp @@ -108,7 +108,8 @@ struct LinearAlgebraFixture : ReferenceFixture { template static void compare(const char* context, const A& non_dist, const A& result, double e) { - BOOST_TEST_CONTEXT(context); + BOOST_TEST_CONTEXT(context) + ; auto diff_with_non_dist = (non_dist("i,j") - result("i,j")).norm().get(); BOOST_CHECK_SMALL(diff_with_non_dist, e); } diff --git a/tests/range_fixture.h b/tests/range_fixture.h index 082ec9e723..3eb9afd611 100644 --- a/tests/range_fixture.h +++ b/tests/range_fixture.h @@ -91,8 +91,12 @@ struct Range1Fixture { }; struct TiledRangeFixtureBase : public Range1Fixture { - TiledRangeFixtureBase() : dims(GlobalFixture::dim, tr1) {} - std::vector dims; + TiledRangeFixtureBase() { + std::fill(dims.begin(), dims.end(), tr1); + std::fill(extents.begin(), extents.end(), tr1.extent()); + } + std::array dims; + std::array extents; }; // struct TiledRangeFixtureBase struct TiledRangeFixture : public RangeFixture, public TiledRangeFixtureBase {