Intrepid2
Intrepid2_TensorViewIterator.hpp
Go to the documentation of this file.
1 // @HEADER
2 // ************************************************************************
3 //
4 // Intrepid2 Package
5 // Copyright (2007) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact Kyungjoo Kim (kyukim@sandia.gov),
38 // Mauro Perego (mperego@sandia.gov), or
39 // Nate Roberts (nvrober@sandia.gov)
40 //
41 // ************************************************************************
42 // @HEADER
43 
49 #ifndef Intrepid2_TensorViewIterator_h
50 #define Intrepid2_TensorViewIterator_h
51 
54 
55 #include <Kokkos_Core.hpp>
56 
57 #include <vector>
58 
59 namespace Intrepid2
60 {
73  template<class TensorViewType, class ViewType1, class ViewType2 ,typename ScalarType>
75  {
76  public:
77  enum RankCombinationType : int
78  {
79  DIMENSION_MATCH,
80  TENSOR_PRODUCT,
81  TENSOR_CONTRACTION
82  };
83  using RankCombinationViewType = Kokkos::View<RankCombinationType*, typename TensorViewType::device_type>;
84  protected:
85 
86  ViewIterator<TensorViewType, ScalarType> tensor_view_iterator_;
89 
90  RankCombinationViewType rank_combination_types_;
91  public:
111  KOKKOS_INLINE_FUNCTION
112  TensorViewIterator(TensorViewType tensor_view, ViewType1 view1, ViewType2 view2,
113  RankCombinationViewType rank_combination_types)
114  :
115  tensor_view_iterator_(tensor_view),
116  view1_iterator_(view1),
117  view2_iterator_(view2),
118  rank_combination_types_(rank_combination_types)
119  {
120  // rank_combination_type should have length equal to the maximum rank of the views provided
121  /*
122  Examples:
123  1. vector dot product in third dimension: {DIMENSION_MATCH, DIMENSION_MATCH, TENSOR_CONTRACTION}
124  - view1 and view2 should both be rank 3, and should match in all dimensions
125  - tensor_view should be rank 2, and should match view1 and view2 in first two dimensions
126  2. vector outer product in third dimension: {DIMENSION_MATCH, DIMENSION_MATCH, TENSOR_PRODUCT}
127  - view1 and view2 should both be rank 3, and should match in first two dimensions
128  - tensor_view should be rank 3, and should match view1 and view2 in first two dimensions
129  - in third dimension, tensor_view should have dimension equal to the product of the third dimension of view1 and the third dimension of view2
130  3. rank-3 view1 treated as vector times scalar rank-2 view2: {DIMENSION_MATCH, DIMENSION_MATCH, TENSOR_PRODUCT}
131  - here, the rank-2 view2 is interpreted as having an extent 1 third dimension
132 
133  We only allow TENSOR_CONTRACTION in final dimension(s)
134  */
135  // check that the above rules are satisfied:
136  unsigned max_component_rank = (view1.rank() > view2.rank()) ? view1.rank() : view2.rank();
137  unsigned max_rank = (tensor_view.rank() > max_component_rank) ? tensor_view.rank() : max_component_rank;
138 
139  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");
140 
141  unsigned expected_rank = 0;
142  bool contracting = false;
143  for (unsigned d=0; d<rank_combination_types.extent(0); d++)
144  {
145  if (rank_combination_types[d] == TENSOR_CONTRACTION)
146  {
147  // check that view1 and view2 agree on the length of this dimension
148  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");
149  contracting = true;
150  }
151  else
152  {
153  if (!contracting)
154  {
155  expected_rank++;
156  if (rank_combination_types[d] == TENSOR_PRODUCT)
157  {
158  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");
159  }
160  else // matching
161  {
162  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");
163  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");
164  }
165  }
166  else
167  {
168  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");
169  }
170  }
171  }
172  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(expected_rank != tensor_view.rank(), std::invalid_argument, "Tensor view does not match expected rank");
173  }
174 
177  KOKKOS_INLINE_FUNCTION
179  {
180  int view2_next_increment_rank = view2_iterator_.nextIncrementRank();
181  int view1_next_increment_rank = view1_iterator_.nextIncrementRank();
182  if (view2_next_increment_rank > view1_next_increment_rank) return view2_next_increment_rank;
183  else return view1_next_increment_rank;
184  }
185 
188  KOKKOS_INLINE_FUNCTION
189  int increment()
190  {
191  // proceed to the next view1/view2 combination
192  // where we're doing a dimension match, then all three iterators should increment in tandem
193  // where we're doing a contraction, view1/view2 should increment in tandem, while tensor_view should be fixed
194  // where we're doing a tensor product, view1 and tensor_view increment in tandem, while view2 is fixed
195 
196  // note that regardless of the choice, view1 should be incremented, with one exception:
197  // If we are doing a tensor product, then view1 can be understood to be in an interior for loop, and it should loop around.
198  // We can detect this by checking which the least rank that would be updated -- if view2's least rank exceeds view1's, then:
199  // - view1 should be reset, AND
200  // - view2 should be incremented (as should the tensor view)
201  int view2_next_increment_rank = view2_iterator_.nextIncrementRank();
202  int view1_next_increment_rank = view1_iterator_.nextIncrementRank();
203  if (view2_next_increment_rank > view1_next_increment_rank)
204  {
205  // if we get here, we should be doing a tensor product in the view2 rank that will change
206  device_assert(rank_combination_types_[view2_next_increment_rank]==TENSOR_PRODUCT);
207  view1_iterator_.reset(view2_next_increment_rank); // set to 0 from the tensor product rank inward -- this is "looping around"
208  view2_iterator_.increment();
209  tensor_view_iterator_.increment();
210  return view2_next_increment_rank;
211  }
212  else
213  {
214  int view1_rank_change = view1_iterator_.increment();
215  if (view1_rank_change >= 0)
216  {
217  switch (rank_combination_types_[view1_rank_change])
218  {
219  case DIMENSION_MATCH:
220  view2_iterator_.increment();
221  tensor_view_iterator_.increment();
222  break;
223  case TENSOR_PRODUCT:
224  // view1 increments fastest; the only time we increment view2 is when view1 loops around; we handle that above
225  tensor_view_iterator_.increment();
226  break;
227  case TENSOR_CONTRACTION:
228  // view1 and view2 increment in tandem; we don't increment tensor_view while contraction is taking place
229  view2_iterator_.increment();
230  }
231  }
232  return view1_rank_change;
233  }
234  }
235 
238  KOKKOS_INLINE_FUNCTION
239  void setLocation(const Kokkos::Array<int,7> location)
240  {
241  view1_iterator_.setLocation(location);
242  view2_iterator_.setLocation(location);
243  tensor_view_iterator_.setLocation(location);
244  }
245 
249  KOKKOS_INLINE_FUNCTION
250  void setLocation(Kokkos::Array<int,7> location1, Kokkos::Array<int,7> location2)
251  {
252  view1_iterator_.setLocation(location1);
253  view2_iterator_.setLocation(location2);
254  Kokkos::Array<int,7> tensor_location = location1;
255  for (unsigned d=0; d<rank_combination_types_.extent(0); d++)
256  {
257  switch (rank_combination_types_[d])
258  {
259  case TENSOR_PRODUCT:
260  // view1 index is fastest-moving:
261  tensor_location[d] = location2[d] * view1_iterator_.getExtent(d) + location1[d];
262  break;
263  case DIMENSION_MATCH:
264  // we copied location1 into tensor_location to initialize -- that's correct in this dimension
265  break;
266  case TENSOR_CONTRACTION:
267  tensor_location[d] = 0;
268  break;
269  }
270  }
271 #ifdef HAVE_INTREPID2_DEBUG
272  // check that the location makes sense
273  for (unsigned d=0; d<rank_combination_types_.extent(0); d++)
274  {
275  switch (rank_combination_types_[d])
276  {
277  case TENSOR_PRODUCT:
278  // in this case, the two indices are independent
279  break;
280  case DIMENSION_MATCH:
281  case TENSOR_CONTRACTION:
282  device_assert(location1[d] == location2[d]);
283  break;
284  }
285  // let's check that the indices are in bounds:
286  device_assert(location1[d] < view1_iterator_.getExtent(d));
287  device_assert(location2[d] < view2_iterator_.getExtent(d));
288  device_assert(tensor_location[d] < tensor_view_iterator_.getExtent(d));
289  }
290 #endif
291  tensor_view_iterator_.setLocation(tensor_location);
292  }
293 
296  KOKKOS_INLINE_FUNCTION
297  ScalarType getView1Entry()
298  {
299  return view1_iterator_.get();
300  }
301 
304  KOKKOS_INLINE_FUNCTION
305  ScalarType getView2Entry()
306  {
307  return view2_iterator_.get();
308  }
309 
312  KOKKOS_INLINE_FUNCTION
313  void set(ScalarType value)
314  {
315  tensor_view_iterator_.set(value);
316  }
317  };
318 
319 } // namespace Intrepid2
320 
321 #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...