Intrepid2
Intrepid2_TensorViewIterator.hpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Intrepid2 Package
4 //
5 // Copyright 2007 NTESS and the Intrepid2 contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
15 #ifndef Intrepid2_TensorViewIterator_h
16 #define Intrepid2_TensorViewIterator_h
17 
20 
21 #include <Kokkos_Core.hpp>
22 
23 #include <vector>
24 
25 namespace Intrepid2
26 {
39  template<class TensorViewType, class ViewType1, class ViewType2 ,typename ScalarType>
41  {
42  public:
43  enum RankCombinationType : int
44  {
45  DIMENSION_MATCH,
46  TENSOR_PRODUCT,
47  TENSOR_CONTRACTION
48  };
49  using RankCombinationViewType = Kokkos::View<RankCombinationType*, typename TensorViewType::device_type>;
50  protected:
51 
52  ViewIterator<TensorViewType, ScalarType> tensor_view_iterator_;
55 
56  RankCombinationViewType rank_combination_types_;
57  public:
77  KOKKOS_INLINE_FUNCTION
78  TensorViewIterator(TensorViewType tensor_view, ViewType1 view1, ViewType2 view2,
79  RankCombinationViewType rank_combination_types)
80  :
81  tensor_view_iterator_(tensor_view),
82  view1_iterator_(view1),
83  view2_iterator_(view2),
84  rank_combination_types_(rank_combination_types)
85  {
86  // rank_combination_type should have length equal to the maximum rank of the views provided
87  /*
88  Examples:
89  1. vector dot product in third dimension: {DIMENSION_MATCH, DIMENSION_MATCH, TENSOR_CONTRACTION}
90  - view1 and view2 should both be rank 3, and should match in all dimensions
91  - tensor_view should be rank 2, and should match view1 and view2 in first two dimensions
92  2. vector outer product in third dimension: {DIMENSION_MATCH, DIMENSION_MATCH, TENSOR_PRODUCT}
93  - view1 and view2 should both be rank 3, and should match in first two dimensions
94  - tensor_view should be rank 3, and should match view1 and view2 in first two dimensions
95  - in third dimension, tensor_view should have dimension equal to the product of the third dimension of view1 and the third dimension of view2
96  3. rank-3 view1 treated as vector times scalar rank-2 view2: {DIMENSION_MATCH, DIMENSION_MATCH, TENSOR_PRODUCT}
97  - here, the rank-2 view2 is interpreted as having an extent 1 third dimension
98 
99  We only allow TENSOR_CONTRACTION in final dimension(s)
100  */
101  // check that the above rules are satisfied:
102  unsigned max_component_rank = (view1.rank() > view2.rank()) ? view1.rank() : view2.rank();
103  unsigned max_rank = (tensor_view.rank() > max_component_rank) ? tensor_view.rank() : max_component_rank;
104 
105  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(rank_combination_types.extent(0) != max_rank, std::invalid_argument, "need to provide RankCombinationType for the largest-rank View");
106 
107  unsigned expected_rank = 0;
108  bool contracting = false;
109  for (unsigned d=0; d<rank_combination_types.extent(0); d++)
110  {
111  if (rank_combination_types[d] == TENSOR_CONTRACTION)
112  {
113  // check that view1 and view2 agree on the length of this dimension
114  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(view1.extent_int(d) != view2.extent_int(d), std::invalid_argument, "Contractions can only occur along ranks of equal length");
115  contracting = true;
116  }
117  else
118  {
119  if (!contracting)
120  {
121  expected_rank++;
122  if (rank_combination_types[d] == TENSOR_PRODUCT)
123  {
124  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(tensor_view.extent_int(d) != view1.extent_int(d) * view2.extent_int(d), std::invalid_argument, "For TENSOR_PRODUCT rank combination, the tensor View must have length in that dimension equal to the product of the two component views in that dimension");
125  }
126  else // matching
127  {
128  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(view1.extent_int(d) != view2.extent_int(d), std::invalid_argument, "For DIMENSION_MATCH rank combination, all three views must have length equal to each other in that rank");
129  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(tensor_view.extent_int(d) != view1.extent_int(d), std::invalid_argument, "For DIMENSION_MATCH rank combination, all three views must have length equal to each other in that rank");
130  }
131  }
132  else
133  {
134  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(contracting, std::invalid_argument, "encountered a non-contraction rank combination after a contraction; contractions can only go at the end");
135  }
136  }
137  }
138  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(expected_rank != tensor_view.rank(), std::invalid_argument, "Tensor view does not match expected rank");
139  }
140 
143  KOKKOS_INLINE_FUNCTION
145  {
146  int view2_next_increment_rank = view2_iterator_.nextIncrementRank();
147  int view1_next_increment_rank = view1_iterator_.nextIncrementRank();
148  if (view2_next_increment_rank > view1_next_increment_rank) return view2_next_increment_rank;
149  else return view1_next_increment_rank;
150  }
151 
154  KOKKOS_INLINE_FUNCTION
155  int increment()
156  {
157  // proceed to the next view1/view2 combination
158  // where we're doing a dimension match, then all three iterators should increment in tandem
159  // where we're doing a contraction, view1/view2 should increment in tandem, while tensor_view should be fixed
160  // where we're doing a tensor product, view1 and tensor_view increment in tandem, while view2 is fixed
161 
162  // note that regardless of the choice, view1 should be incremented, with one exception:
163  // If we are doing a tensor product, then view1 can be understood to be in an interior for loop, and it should loop around.
164  // We can detect this by checking which the least rank that would be updated -- if view2's least rank exceeds view1's, then:
165  // - view1 should be reset, AND
166  // - view2 should be incremented (as should the tensor view)
167  int view2_next_increment_rank = view2_iterator_.nextIncrementRank();
168  int view1_next_increment_rank = view1_iterator_.nextIncrementRank();
169  if (view2_next_increment_rank > view1_next_increment_rank)
170  {
171  // if we get here, we should be doing a tensor product in the view2 rank that will change
172  device_assert(rank_combination_types_[view2_next_increment_rank]==TENSOR_PRODUCT);
173  view1_iterator_.reset(view2_next_increment_rank); // set to 0 from the tensor product rank inward -- this is "looping around"
174  view2_iterator_.increment();
175  tensor_view_iterator_.increment();
176  return view2_next_increment_rank;
177  }
178  else
179  {
180  int view1_rank_change = view1_iterator_.increment();
181  if (view1_rank_change >= 0)
182  {
183  switch (rank_combination_types_[view1_rank_change])
184  {
185  case DIMENSION_MATCH:
186  view2_iterator_.increment();
187  tensor_view_iterator_.increment();
188  break;
189  case TENSOR_PRODUCT:
190  // view1 increments fastest; the only time we increment view2 is when view1 loops around; we handle that above
191  tensor_view_iterator_.increment();
192  break;
193  case TENSOR_CONTRACTION:
194  // view1 and view2 increment in tandem; we don't increment tensor_view while contraction is taking place
195  view2_iterator_.increment();
196  }
197  }
198  return view1_rank_change;
199  }
200  }
201 
204  KOKKOS_INLINE_FUNCTION
205  void setLocation(const Kokkos::Array<int,7> location)
206  {
207  view1_iterator_.setLocation(location);
208  view2_iterator_.setLocation(location);
209  tensor_view_iterator_.setLocation(location);
210  }
211 
215  KOKKOS_INLINE_FUNCTION
216  void setLocation(Kokkos::Array<int,7> location1, Kokkos::Array<int,7> location2)
217  {
218  view1_iterator_.setLocation(location1);
219  view2_iterator_.setLocation(location2);
220  Kokkos::Array<int,7> tensor_location = location1;
221  for (unsigned d=0; d<rank_combination_types_.extent(0); d++)
222  {
223  switch (rank_combination_types_[d])
224  {
225  case TENSOR_PRODUCT:
226  // view1 index is fastest-moving:
227  tensor_location[d] = location2[d] * view1_iterator_.getExtent(d) + location1[d];
228  break;
229  case DIMENSION_MATCH:
230  // we copied location1 into tensor_location to initialize -- that's correct in this dimension
231  break;
232  case TENSOR_CONTRACTION:
233  tensor_location[d] = 0;
234  break;
235  }
236  }
237 #ifdef HAVE_INTREPID2_DEBUG
238  // check that the location makes sense
239  for (unsigned d=0; d<rank_combination_types_.extent(0); d++)
240  {
241  switch (rank_combination_types_[d])
242  {
243  case TENSOR_PRODUCT:
244  // in this case, the two indices are independent
245  break;
246  case DIMENSION_MATCH:
247  case TENSOR_CONTRACTION:
248  device_assert(location1[d] == location2[d]);
249  break;
250  }
251  // let's check that the indices are in bounds:
252  device_assert(location1[d] < view1_iterator_.getExtent(d));
253  device_assert(location2[d] < view2_iterator_.getExtent(d));
254  device_assert(tensor_location[d] < tensor_view_iterator_.getExtent(d));
255  }
256 #endif
257  tensor_view_iterator_.setLocation(tensor_location);
258  }
259 
262  KOKKOS_INLINE_FUNCTION
263  ScalarType getView1Entry()
264  {
265  return view1_iterator_.get();
266  }
267 
270  KOKKOS_INLINE_FUNCTION
271  ScalarType getView2Entry()
272  {
273  return view2_iterator_.get();
274  }
275 
278  KOKKOS_INLINE_FUNCTION
279  void set(ScalarType value)
280  {
281  tensor_view_iterator_.set(value);
282  }
283  };
284 
285 } // namespace Intrepid2
286 
287 #endif /* Intrepid2_TensorViewIterator_h */
KOKKOS_INLINE_FUNCTION ScalarType getView1Entry()
KOKKOS_INLINE_FUNCTION void reset(unsigned from_rank_number=0)
KOKKOS_INLINE_FUNCTION int nextIncrementRank()
KOKKOS_INLINE_FUNCTION int getExtent(int dimension)
KOKKOS_INLINE_FUNCTION TensorViewIterator(TensorViewType tensor_view, ViewType1 view1, ViewType2 view2, RankCombinationViewType rank_combination_types)
Constructor.
#define INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(test, x, msg)
KOKKOS_INLINE_FUNCTION ScalarType get()
KOKKOS_INLINE_FUNCTION void setLocation(Kokkos::Array< int, 7 > location1, Kokkos::Array< int, 7 > location2)
Implementation of an assert that can safely be called from device code.
Iterator allows linear traversal of (part of) a Kokkos View in a manner that is agnostic to its rank...
KOKKOS_INLINE_FUNCTION void set(const ScalarType &value)
KOKKOS_INLINE_FUNCTION void set(ScalarType value)
KOKKOS_INLINE_FUNCTION void setLocation(const Kokkos::Array< int, 7 > location)
KOKKOS_INLINE_FUNCTION ScalarType getView2Entry()
KOKKOS_INLINE_FUNCTION int increment()
KOKKOS_INLINE_FUNCTION void setLocation(const Kokkos::Array< int, 7 > &location)
KOKKOS_INLINE_FUNCTION int nextIncrementRank()
A helper class that allows iteration over three Kokkos Views simultaneously, according to tensor comb...