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
48
49#ifndef Intrepid2_TensorViewIterator_h
50#define Intrepid2_TensorViewIterator_h
51
54
55#include <Kokkos_Core.hpp>
56
57#include <vector>
58
59namespace Intrepid2
60{
73 template<class TensorViewType, class ViewType1, class ViewType2 ,typename ScalarType>
75 {
76 public:
77 enum RankCombinationType
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
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 */
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.
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...