diagonal_array.h
Go to the documentation of this file.
1 /*
2  * This file is a part of TiledArray.
3  * Copyright (C) 2016 Virginia Tech
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * Drew Lewis
19  * Department of Chemistry, Virginia Tech
20  *
21  * diagonal_array.h
22  * Nov 30, 2016
23  *
24  */
25 
26 #ifndef TILEDARRAY_SPECIALARRAYS_DIAGONAL_ARRAY_H__INCLUDED
27 #define TILEDARRAY_SPECIALARRAYS_DIAGONAL_ARRAY_H__INCLUDED
28 
29 #include <TiledArray/dist_array.h>
30 #include <TiledArray/range.h>
31 #include <TiledArray/tensor.h>
32 #include <TiledArray/tiled_range.h>
33 
34 #include <vector>
35 
36 namespace TiledArray {
37 namespace detail {
38 
40 
45 inline Range diagonal_range(Range const &rng) {
46  const auto rank = rng.rank();
47  TA_ASSERT(rng.rank() > 0);
48  auto lo = rng.lobound_data();
49  auto up = rng.upbound_data();
50 
51  // Determine the largest lower index and the smallest upper index
52  auto max_low = *std::max_element(lo, lo + rank);
53  auto min_up = *std::min_element(up, up + rank);
54 
55  // If the max small elem is less than the min large elem then a diagonal
56  // elem is in this tile;
57  if (max_low < min_up) {
58  return Range({max_low}, {min_up});
59  } else {
60  return Range();
61  }
62 }
63 
69 template <typename T>
70 Tensor<float> diagonal_shape(TiledRange const &trange, T val) {
71  Tensor<float> shape(trange.tiles_range(), 0.0);
72 
73  auto ext = trange.elements_range().extent();
74  auto diag_extent = *std::min_element(std::begin(ext), std::end(ext));
75 
76  auto ndim = trange.rank();
77  auto diag_elem = 0ul;
78  // the diagonal elements will never be larger than the length of the
79  // shortest dimension
80  while (diag_elem < diag_extent) {
81  // Get the tile index corresponding to the current diagonal_elem
82  auto tile_idx = trange.element_to_tile(std::vector<int>(ndim, diag_elem));
83  auto tile_range = trange.make_tile_range(tile_idx);
84 
85  // Compute the range of diagonal elements in the tile
86  auto d_range = diagonal_range(tile_range);
87 
88  // Since each diag elem has the same value the norm of the tile is
89  // \sqrt{\sum_{diag} val^2} = \sqrt{ndiags * val^2}
90  float t_norm = std::sqrt(val * val * d_range.volume());
91  shape(tile_idx) = t_norm;
92 
93  // Update diag_elem to the next elem not in this tile
94  diag_elem = d_range.upbound_data()[0];
95  }
96 
97  return shape;
98 }
99 
108 template <typename RandomAccessIterator>
109 std::enable_if_t<is_iterator<RandomAccessIterator>::value, Tensor<float>>
110 diagonal_shape(TiledRange const &trange, RandomAccessIterator diagonals_begin,
111  RandomAccessIterator diagonals_end = {}) {
112  const bool have_end = diagonals_end == RandomAccessIterator{};
113 
114  Tensor<float> shape(trange.tiles_range(), 0.0);
115 
116  const auto rank = trange.rank();
117  auto ext = trange.elements_range().extent_data();
118  auto diag_extent = *std::min_element(ext, ext + rank);
119 
120  auto ndim = trange.rank();
121  auto diag_elem = 0ul;
122  // the diagonal elements will never be larger than the length of the
123  // shortest dimension
124  while (diag_elem < diag_extent) {
125  // Get the tile index corresponding to the current diagonal_elem
126  auto tile_idx = trange.element_to_tile(std::vector<int>(ndim, diag_elem));
127  auto tile_range = trange.make_tile_range(tile_idx);
128 
129  // Compute the range of diagonal elements in the tile
130  auto d_range = diagonal_range(tile_range);
131  TA_ASSERT(d_range != Range{});
132  TA_ASSERT(diag_elem == d_range.lobound_data()[0]);
133  const auto beg = diag_elem;
134  const auto end = d_range.upbound_data()[0];
135  if (have_end) {
136  TA_ASSERT(diagonals_begin + beg < diagonals_end);
137  TA_ASSERT(diagonals_begin + end <= diagonals_end);
138  }
139 
140  auto t_norm = std::accumulate(diagonals_begin + beg, diagonals_begin + end,
141  0.0, [](const auto &sum, const auto &val) {
142  const auto abs_val = std::abs(val);
143  return sum + abs_val * abs_val;
144  });
145  shape(tile_idx) = static_cast<float>(t_norm);
146 
147  // Update diag_elem to the next elem not in this tile
148  diag_elem = end;
149  }
150 
151  return shape;
152 }
153 
155 
160 template <typename Array, typename T>
162  using Tile = typename Array::value_type;
163 
164  // Task to create each tile
165  A.init_tiles([val](const Range &rng) {
166  // Compute range of diagonal elements in the tile
167  auto diags = detail::diagonal_range(rng);
168  const auto rank = rng.rank();
169 
170  Tile tile(rng, 0.0);
171 
172  if (diags.volume() > 0) { // If the tile has diagonal elems
173 
174  // Loop over the elements and write val into them
175  auto diag_lo = diags.lobound_data()[0];
176  auto diag_hi = diags.upbound_data()[0];
177  for (auto elem = diag_lo; elem < diag_hi; ++elem) {
178  tile(std::vector<int>(rank, elem)) = val;
179  }
180  }
181 
182  return tile;
183  });
184 }
185 
187 
192 template <typename Array, typename RandomAccessIterator>
193 std::enable_if_t<is_iterator<RandomAccessIterator>::value, void>
194 write_diag_tiles_to_array_rng(Array &A, RandomAccessIterator diagonals_begin) {
195  using Tile = typename Array::value_type;
196 
197  A.init_tiles(
198  // Task to create each tile
199  [diagonals_begin](const Range &rng) {
200  // Compute range of diagonal elements in the tile
201  auto diags = detail::diagonal_range(rng);
202  const auto rank = rng.rank();
203 
204  Tile tile(rng, 0.0);
205 
206  if (diags.volume() > 0) { // If the tile has diagonal elems
207  // Loop over the elements and write val into them
208  auto diag_lo = diags.lobound_data()[0];
209  auto diag_hi = diags.upbound_data()[0];
210  for (auto elem = diag_lo; elem < diag_hi; ++elem) {
211  tile(std::vector<int>(rank, elem)) = *(diagonals_begin + elem);
212  }
213  }
214 
215  return tile;
216  });
217 }
218 
219 } // namespace detail
220 
222 
229 template <typename Array, typename T = double>
230 Array diagonal_array(World &world, TiledRange const &trange, T val = 1) {
231  using Policy = typename Array::policy_type;
232  // Init the array
233  if constexpr (is_dense_v<Policy>) {
234  Array A(world, trange);
236  return A;
237  } else {
238  // Compute shape and init the Array
239  auto shape_norm = detail::diagonal_shape(trange, val);
240  using ShapeType = typename Policy::shape_type;
241  ShapeType shape(shape_norm, trange);
242  Array A(world, trange, shape);
244  return A;
245  }
246  abort(); // unreachable
247 }
248 
250 
260 template <typename Array, typename RandomAccessIterator>
261 std::enable_if_t<detail::is_iterator<RandomAccessIterator>::value, Array>
262 diagonal_array(World &world, TiledRange const &trange,
263  RandomAccessIterator diagonals_begin,
264  RandomAccessIterator diagonals_end = {}) {
265  using Policy = typename Array::policy_type;
266 
267  if (diagonals_end != RandomAccessIterator{}) {
268  const auto rank = trange.rank();
269  auto ext = trange.elements_range().extent_data();
270  [[maybe_unused]] auto diag_extent = *std::min_element(ext, ext + rank);
271  TA_ASSERT(diagonals_begin + diag_extent <= diagonals_end);
272  }
273 
274  // Init the array
275  if constexpr (is_dense_v<Policy>) {
276  Array A(world, trange);
277  detail::write_diag_tiles_to_array_rng(A, diagonals_begin);
278  return A;
279  } else {
280  // Compute shape and init the Array
281  auto shape_norm =
282  detail::diagonal_shape(trange, diagonals_begin, diagonals_end);
283  using ShapeType = typename Policy::shape_type;
284  ShapeType shape(shape_norm, trange);
285  Array A(world, trange, shape);
286  detail::write_diag_tiles_to_array_rng(A, diagonals_begin);
287  return A;
288  }
289  abort(); // unreachable
290 }
291 
292 } // namespace TiledArray
293 
294 #endif // TILEDARRAY_SPECIALARRAYS_DIAGONAL_ARRAY_H__INCLUDED
const index1_type * lobound_data() const
Range lower bound data accessor.
Definition: range.h:685
impl_type::value_type value_type
Tile type.
Definition: dist_array.h:86
const range_type & tiles_range() const
Access the tile range.
Definition: tiled_range.h:147
std::enable_if_t< detail::is_integral_range_v< Index >, typename range_type::index > element_to_tile(const Index &index) const
Convert an element index to a tile index.
Definition: tiled_range.h:259
void write_diag_tiles_to_array_val(Array &A, T val)
Writes tiles of a constant diagonal array.
const index1_type * upbound_data() const
Range upper bound data accessor.
Definition: range.h:710
range_type make_tile_range(const index1_type &i) const
Construct a range for the tile indexed by the given ordinal index.
Definition: tiled_range.h:180
constexpr auto end(Eigen::Matrix< _Scalar, _Rows, 1, _Options, _MaxRows, 1 > &m)
Definition: eigen.h:51
auto rank(const DistArray< Tile, Policy > &a)
Definition: dist_array.h:1617
const range_type & elements_range() const
Access the element range.
Definition: tiled_range.h:158
std::enable_if_t< is_iterator< RandomAccessIterator >::value, void > write_diag_tiles_to_array_rng(Array &A, RandomAccessIterator diagonals_begin)
Writes tiles of a nonconstant diagonal array.
Array diagonal_array(World &world, TiledRange const &trange, T val=1)
Creates a constant diagonal DistArray.
std::size_t rank() const
The rank accessor.
Definition: tiled_range.h:277
#define TA_ASSERT(EXPR,...)
Definition: error.h:39
index_view_type extent() const
Range extent accessor.
Definition: range.h:741
Range data of a tiled array.
Definition: tiled_range.h:32
DistArray< Tile, Policy > Array
impl_type::policy_type policy_type
Policy type.
Definition: dist_array.h:62
Range diagonal_range(Range const &rng)
Computes a range of the diagonal elements (if any) in a rank-d Range.
Tensor< float > diagonal_shape(TiledRange const &trange, T val)
computes shape data (i.e. Frobenius norms of the tiles) for a constant diagonal tensor
Forward declarations.
Definition: dist_array.h:57
auto abs(const ComplexConjugate< T > &a)
Definition: complex.h:270
void init_tiles(Op &&op, bool skip_set=false)
Initialize (local) tiles with a user provided functor.
Definition: dist_array.h:834
decltype(auto) sum(const Tile< Arg > &arg)
Sum the elements of a tile.
Definition: tile.h:1496
An N-dimensional tensor object.
Definition: tensor.h:50
const index1_type * extent_data() const
Range extent data accessor.
Definition: range.h:735
unsigned int rank() const
Rank accessor.
Definition: range.h:669
An N-dimensional shallow copy wrapper for tile objects.
Definition: tile.h:82
A (hyperrectangular) interval on , space of integer -indices.
Definition: range.h:46