vector_of_arrays.h
Go to the documentation of this file.
1 //
2 // Created by Chong Peng on 2019-05-01.
3 //
4 
5 #ifndef TILEDARRAY_CONVERSIONS_VECTOR_OF_ARRAYS_H_
6 #define TILEDARRAY_CONVERSIONS_VECTOR_OF_ARRAYS_H_
7 
8 #include <tiledarray.h>
9 
10 namespace TiledArray {
11 
12 namespace detail {
13 
15 
18 
25  std::size_t array_rank, const TiledArray::TiledRange& array_trange,
26  std::size_t block_size = 1) {
28  TA::TiledRange1 new_trange1;
29  {
30  std::vector<std::size_t> new_trange1_v;
31  auto range_size = array_rank;
32  new_trange1_v.push_back(0);
33  for (decltype(range_size) i = block_size; i < range_size; i += block_size) {
34  new_trange1_v.push_back(i);
35  }
36  new_trange1_v.push_back(range_size);
37  new_trange1 = TA::TiledRange1(new_trange1_v.begin(), new_trange1_v.end());
38  }
39 
41  TA::TiledRange new_trange;
42  {
43  auto old_trange1s = array_trange.data();
44  old_trange1s.insert(old_trange1s.begin(), new_trange1);
45  new_trange = TA::TiledRange(old_trange1s.begin(), old_trange1s.end());
46  }
47 
48  return new_trange;
49 }
50 
70 template <typename Tile>
72  madness::World& global_world,
74  const std::size_t array_rank, const TA::TiledRange& fused_trange) {
75  if (arrays.size() == 0) {
76  TA::Tensor<float> fused_tile_norms(fused_trange.tiles_range(), 0.f);
77  return TA::SparseShape<float>(global_world, fused_tile_norms, fused_trange,
78  true);
79  }
80  const std::size_t rank = global_world.rank();
81  auto size = global_world.size();
82  auto first_tile_in_mode0 = *fused_trange.dim(0).begin();
83  const auto block_size =
84  first_tile_in_mode0.second - first_tile_in_mode0.first;
85 
86  std::size_t ntiles_per_array = arrays[0].trange().tiles_range().volume();
87  // precompute tile volumes for later repeated use
88  std::vector<size_t> tile_volumes(ntiles_per_array);
89  {
90  const auto& tiles_range = arrays[0].trange().tiles_range();
91  for (auto&& tile_idx : tiles_range) {
92  const auto tile_ord = tiles_range.ordinal(tile_idx);
93  tile_volumes[tile_ord] =
94  arrays[0].trange().make_tile_range(tile_idx).volume();
95  }
96  }
97 
98  TA::Tensor<float> fused_tile_norms(
99  fused_trange.tiles_range(), 0.f); // use nonzeroes for local tiles only
100 
101  // compute norms of fused tiles
102  // N.B. tile norms are stored in scaled format, unscale in order to compute
103  // norms of fused tiles
104  std::size_t narrays = array_rank;
105  size_t fused_tile_ord = 0;
106  auto element_offset_in_owner = 0;
107  for (size_t vidx = 0, fused_vidx = 0; vidx < narrays;
108  vidx += block_size, ++fused_vidx) {
109  bool have_rank = (rank == fused_vidx % size);
110  // how many arrays actually constribute to this fused tile ... last fused
111  // tile may have fewer than block_size
112  if (have_rank) {
113  const auto vblk_size =
114  (narrays - vidx) >= block_size ? block_size : narrays - vidx;
115  for (size_t tile_ord = 0; tile_ord != ntiles_per_array;
116  ++tile_ord, ++fused_tile_ord) {
117  auto array_ptr = arrays.begin() + element_offset_in_owner * vblk_size;
118  float unscaled_fused_tile_norm2 = 0;
119  const auto tile_volume = tile_volumes[tile_ord];
120  for (size_t v = 0, vv = vidx; v != vblk_size; ++v, ++vv) {
121  const auto unscaled_tile_norm =
122  (*(array_ptr)).shape().data()[tile_ord] * tile_volume;
123  unscaled_fused_tile_norm2 += unscaled_tile_norm * unscaled_tile_norm;
124  ++array_ptr;
125  }
126  const auto fused_tile_volume = tile_volume * vblk_size;
127  const auto fused_tile_norm =
128  std::sqrt(unscaled_fused_tile_norm2) / fused_tile_volume;
129 
130  *(fused_tile_norms.data() + fused_tile_ord) = fused_tile_norm;
131  }
132  element_offset_in_owner += 1;
133  } else {
134  fused_tile_ord += ntiles_per_array;
135  }
136  }
137 
138  auto fused_shapes = TA::SparseShape<float>(global_world, fused_tile_norms,
139  fused_trange, true);
140 
141  return fused_shapes;
142 }
143 
158 template <typename Tile>
160  madness::World&,
162  const std::size_t array_rank, const TA::TiledRange& fused_trange) {
163  return TA::DenseShape(1, fused_trange);
164 }
165 
168 
179 // TODO rename to tilewise_slice_of_fused_shape
181  const TA::TiledRange& split_trange,
182  const TA::SparsePolicy::shape_type& shape, const std::size_t tile_idx,
183  const std::size_t split_ntiles, const std::size_t tile_size) {
184  TA_ASSERT(split_ntiles == split_trange.tiles_range().volume());
185  TA::Tensor<float> split_tile_norms(split_trange.tiles_range());
186 
187  // map element i to its tile index
188  std::size_t offset = tile_idx * split_ntiles;
189 
190  // note that unlike fusion we cannot compute exact norm of the split tile
191  // to guarantee upper bound we have to multiply the norms by the number of
192  // split tiles in the fused tile; to see why multiplication is necessary think
193  // of a tile obtained by fusing 1 nonzero tile with one or more zero tiles.
194  const auto* split_tile_begin = shape.data().data() + offset;
195  std::transform(split_tile_begin, split_tile_begin + split_ntiles,
196  split_tile_norms.data(),
197  [tile_size](const float& elem) { return elem * tile_size; });
198 
199  auto split_shape =
200  TA::SparseShape<float>(split_tile_norms, split_trange, true);
201  return split_shape;
202 }
203 
206 
213  const TA::TiledRange& split_trange,
214  const TA::DensePolicy::shape_type& shape, const std::size_t tile_idx,
215  const std::size_t split_ntiles, const std::size_t tile_size) {
216  return TA::DenseShape(tile_size, split_trange);
217 }
218 } // namespace detail
219 
220 namespace detail {
223 template <typename Array>
225  : public madness::WorldObject<dist_subarray_vec<Array>> {
226  public:
227  using Tile = typename Array::value_type;
228  using Policy = typename Array::policy_type;
229 
235  dist_subarray_vec(madness::World& world, const std::vector<Array>& array,
236  const std::size_t rank)
237  : madness::WorldObject<dist_subarray_vec<Array>>(world),
238  split_array(array),
239  rank_(rank) {
240  this->process_pending();
241  }
242 
243  virtual ~dist_subarray_vec() {}
244 
249  template <typename Index>
250  madness::Future<Tile> get_tile(int r, Index& i) {
251  return split_array.at(r).find(i);
252  }
253 
255  const std::vector<Array>& array_accessor() const { return split_array; }
256 
258  unsigned long size() const { return rank_; }
259 
260  private:
261  const std::vector<Array>& split_array;
262  const int rank_;
263 };
264 } // namespace detail
265 
268 
288 template <typename Tile, typename Policy>
290  madness::World& global_world,
291  const std::vector<TA::DistArray<Tile, Policy>>& array_vec,
292  const std::size_t fused_dim_extent,
293  const TiledArray::TiledRange& array_trange, std::size_t block_size = 1) {
294  auto nproc = global_world.size();
295 
296  // make instances of array_vec globally accessible
298  detail::dist_subarray_vec<Array> arrays(global_world, array_vec,
299  fused_dim_extent);
300 
301  // make fused tiledrange
302  auto fused_trange =
303  detail::prepend_dim_to_trange(fused_dim_extent, array_trange, block_size);
304  std::size_t ntiles_per_array = array_trange.tiles_range().volume();
305 
306  // make fused shape
307  auto fused_shape = detail::fuse_vector_of_shapes_tiles(
308  global_world, arrays.array_accessor(), fused_dim_extent, fused_trange);
309 
310  // make fused array
311  TA::DistArray<Tile, Policy> fused_array(global_world, fused_trange,
312  fused_shape);
313 
315  auto make_tile = [](const TA::Range& range,
316  const std::vector<madness::Future<Tile>>& tiles) {
317  TA_ASSERT(range.extent(0) == tiles.size());
318  Tile result(range);
319  const auto volume = range.volume();
320  size_t result_volume = 0;
321  auto* result_ptr = result.data();
322  for (auto&& fut_of_tile : tiles) {
323  TA_ASSERT(fut_of_tile.probe());
324  const auto& tile = fut_of_tile.get();
325  const auto* tile_data = tile.data();
326  const auto tile_volume = tile.size();
327  std::copy(tile_data, tile_data + tile_volume, result_ptr);
328  result_ptr += tile_volume;
329  result_volume += tile_volume;
330  }
331  TA_ASSERT(volume == result_volume);
332  return result;
333  };
334 
336  for (auto&& fused_tile_ord : *fused_array.pmap()) {
337  if (!fused_array.is_zero(fused_tile_ord)) {
338  // convert ordinal of the fused tile to the ordinals of its constituent
339  // tiles
340  const auto div0 = std::ldiv(fused_tile_ord, ntiles_per_array);
341  // index of the 0th mode of this tile
342  const auto tile_idx_mode0 = div0.quot;
343  // ordinal of the corresponding tile in the unfused array
344  const auto tile_ord_array = div0.rem;
345 
346  const auto div1 = std::ldiv(tile_idx_mode0, nproc);
347  const auto tile_idx_on_owner = div1.quot;
348  const auto vector_idx_offset_on_owner = tile_idx_on_owner * block_size;
349  const auto owner_rank = div1.rem;
350 
351  auto fused_tile_range =
352  fused_array.trange().make_tile_range(fused_tile_ord);
353  // make a vector of Futures to the input tiles
354  std::vector<madness::Future<Tile>> input_tiles;
355  input_tiles.reserve(fused_tile_range.extent(0));
356  for (size_t v = 0, vidx = tile_idx_mode0 * block_size;
357  v != block_size && vidx < fused_dim_extent; ++v, ++vidx) {
358  using Index = decltype(tile_ord_array);
359  input_tiles.emplace_back(
360  arrays.task(owner_rank,
362  DistArray<Tile, Policy>>::template get_tile<Index>,
363  vector_idx_offset_on_owner + v, tile_ord_array));
364  }
365  fused_array.set(
366  fused_tile_ord,
367  global_world.taskq.add(make_tile, std::move(fused_tile_range),
368  std::move(input_tiles)));
369  }
370  }
371 
372  // keep arrays around until everyone is done
373  global_world.gop.fence();
374 
375  return fused_array;
376 }
377 
380 
392 template <typename Tile, typename Policy>
394  madness::World& local_world, const TA::DistArray<Tile, Policy>& fused_array,
395  std::size_t tile_idx,
397  const TA::TiledRange& split_trange) {
398  TA_ASSERT(tile_idx < fused_array.trange().dim(0).extent());
399  auto arrays_size = split_arrays.size();
400 
401  // calculate the number of elements in the 0th dimension are in this tile
402  auto tile_range = fused_array.trange().dim(0).tile(tile_idx);
403  auto tile_size = tile_range.second - tile_range.first;
404  std::size_t split_ntiles = split_trange.tiles_range().volume();
405  auto& shape = fused_array.shape();
406 
407  // Create tile_size arrays and put them into split_arrays
408  for (size_t i = tile_range.first; i < tile_range.second; ++i) {
409  auto split_shape = detail::subshape_from_fused_tile(
410  split_trange, shape, tile_idx, split_ntiles, tile_size);
411  // create split Array object
412  TA::DistArray<Tile, Policy> split_array(local_world, split_trange,
413  split_shape);
414  split_arrays.push_back(split_array);
415  }
416 
418  auto make_tile = [](const TA::Range& range, const Tile& fused_tile,
419  const size_t i_offset_in_tile) {
420  const auto split_tile_volume = range.volume();
421  return Tile(range,
422  fused_tile.data() + i_offset_in_tile * split_tile_volume);
423  };
424 
426  auto split_array_ptr = split_arrays.data();
427  for (std::size_t index : *(*split_array_ptr).pmap()) {
428  std::size_t fused_array_index = tile_idx * split_ntiles + index;
429  if (!fused_array.is_zero(fused_array_index)) {
430  for (std::size_t i = tile_range.first, tile_count = 0;
431  i < tile_range.second; ++i, ++tile_count) {
432  auto& array = *(split_array_ptr + arrays_size + tile_count);
433  array.set(index, local_world.taskq.add(
434  make_tile, array.trange().make_tile_range(index),
435  fused_array.find(fused_array_index), tile_count));
436  }
437  }
438  }
439  return;
440 }
441 
442 } // namespace TiledArray
443 
444 #endif // TILEDARRAY_CONVERSIONS_VECTOR_OF_ARRAYS_H_
dist_subarray_vec(madness::World &world, const std::vector< Array > &array, const std::size_t rank)
impl_type::value_type value_type
Tile type.
Definition: dist_array.h:86
TA::DistArray< Tile, Policy > fuse_vector_of_arrays_tiles(madness::World &global_world, const std::vector< TA::DistArray< Tile, Policy >> &array_vec, const std::size_t fused_dim_extent, const TiledArray::TiledRange &array_trange, std::size_t block_size=1)
fuses a vector of DistArray objects, each with the same TiledRange into a DistArray with 1 more dimen...
const std::vector< Array > & array_accessor() const
const range_type & tiles_range() const
Access the tile range.
Definition: tiled_range.h:147
madness::Future< Tile > get_tile(int r, Index &i)
void set(const Index &i, InIter first)
Definition: dist_array.h:619
const TiledRange1 & dim(std::size_t d) const
Accessor of the tiled range for one of the dimensions.
Definition: tiled_range.h:283
auto rank(const DistArray< Tile, Policy > &a)
Definition: dist_array.h:1617
void subarray_from_fused_array(madness::World &local_world, const TA::DistArray< Tile, Policy > &fused_array, std::size_t tile_idx, std::vector< TA::DistArray< Tile, Policy >> &split_arrays, const TA::TiledRange &split_trange)
extracts a subarray of a fused array created with fuse_vector_of_arrays and creates the array in loca...
const_pointer data() const
Data direct access.
Definition: tensor.h:609
size_type size() const
Definition: size_array.h:167
typename Array::policy_type Policy
#define TA_ASSERT(EXPR,...)
Definition: error.h:39
index_view_type extent() const
Range extent accessor.
Definition: range.h:741
TA::SparseShape< float > subshape_from_fused_tile(const TA::TiledRange &split_trange, const TA::SparsePolicy::shape_type &shape, const std::size_t tile_idx, const std::size_t split_ntiles, const std::size_t tile_size)
extracts the shape of a slice of a fused array created with fuse_vector_of_arrays
TA::TiledRange prepend_dim_to_trange(std::size_t array_rank, const TiledArray::TiledRange &array_trange, std::size_t block_size=1)
prepends an extra dimension to a TRange
Range data of a tiled array.
Definition: tiled_range.h:32
const trange_type & trange() const
Tiled range accessor.
Definition: dist_array.h:917
Future< value_type > find(const Index &i) const
Find local or remote tile by index.
Definition: dist_array.h:524
Tile< T > make_tile(T &&t)
Factory function for tiles.
Definition: tile.h:619
const shape_type & shape() const
Shape accessor.
Definition: dist_array.h:1039
impl_type::policy_type policy_type
Policy type.
Definition: dist_array.h:62
Forward declarations.
Definition: dist_array.h:57
TA::SparseShape< float > fuse_vector_of_shapes_tiles(madness::World &global_world, const std::vector< TA::DistArray< Tile, TA::SparsePolicy >> &arrays, const std::size_t array_rank, const TA::TiledRange &fused_trange)
fuses the SparseShape objects of a tilewise-round-robin distributed vector of Arrays into single Spar...
Dense shape of an array.
Definition: dense_shape.h:53
const index1_type * data() const
Definition: range.h:83
bool is_zero(const Index &i) const
Check for zero tiles.
Definition: dist_array.h:1137
TiledArray::DenseShape shape_type
Definition: dense_policy.h:44
TiledArray::SparseShape< float > shape_type
Definition: sparse_policy.h:41
size_t volume(const DistArray< Tile, Policy > &a)
Definition: dist_array.h:1622
const_iterator begin() const
Returns an iterator to the first tile in the range.
Definition: tiled_range1.h:128
ordinal_type volume() const
Range volume accessor.
Definition: range.h:783
decltype(auto) data()
Data direct access.
Definition: tile.h:196
const Ranges & data() const
Tile dimension boundary array accessor.
Definition: tiled_range.h:292
Frobenius-norm-based sparse shape.
Definition: sparse_shape.h:74
An N-dimensional tensor object.
Definition: tensor.h:50
std::vector< T > vector
Definition: vector.h:41
const std::shared_ptr< pmap_interface > & pmap() const
Process map accessor.
Definition: dist_array.h:1019
An N-dimensional shallow copy wrapper for tile objects.
Definition: tile.h:82
global view of a tilewise-round-robin distributed std::vector of Arrays
A (hyperrectangular) interval on , space of integer -indices.
Definition: range.h:46