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
25namespace 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
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 */
Implementation of an assert that can safely be called from device code.
KOKKOS_INLINE_FUNCTION void device_assert(bool val)
#define INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(test, x, msg)
Iterator allows linear traversal of (part of) a Kokkos View in a manner that is agnostic to its rank.
A helper class that allows iteration over three Kokkos Views simultaneously, according to tensor comb...
KOKKOS_INLINE_FUNCTION void setLocation(Kokkos::Array< int, 7 > location1, Kokkos::Array< int, 7 > location2)
KOKKOS_INLINE_FUNCTION ScalarType getView1Entry()
KOKKOS_INLINE_FUNCTION int nextIncrementRank()
KOKKOS_INLINE_FUNCTION TensorViewIterator(TensorViewType tensor_view, ViewType1 view1, ViewType2 view2, RankCombinationViewType rank_combination_types)
Constructor.
KOKKOS_INLINE_FUNCTION void setLocation(const Kokkos::Array< int, 7 > location)
KOKKOS_INLINE_FUNCTION void set(ScalarType value)
KOKKOS_INLINE_FUNCTION ScalarType getView2Entry()
A helper class that allows iteration over some part of a Kokkos View, while allowing the calling code...
KOKKOS_INLINE_FUNCTION int nextIncrementRank()
KOKKOS_INLINE_FUNCTION int getExtent(int dimension)
KOKKOS_INLINE_FUNCTION void set(const ScalarType &value)
KOKKOS_INLINE_FUNCTION int increment()
KOKKOS_INLINE_FUNCTION ScalarType get()
KOKKOS_INLINE_FUNCTION void setLocation(const Kokkos::Array< int, 7 > &location)
KOKKOS_INLINE_FUNCTION void reset(unsigned from_rank_number=0)