OPAL (Object Oriented Parallel Accelerator Library) 2022.1
OPAL
Index.h
Go to the documentation of this file.
1// -*- C++ -*-
2/***************************************************************************
3 *
4 * The IPPL Framework
5 *
6 *
7 * Visit http://people.web.psi.ch/adelmann/ for more details
8 *
9 ***************************************************************************/
10
11#ifndef INDEX_H
12#define INDEX_H
13
14/***********************************************************************
15
16Define a slice in an array.
17
18This essentially defines a list of evenly spaced numbers.
19Most commonly this list will be increasing (positive stride)
20but it can also have negative stride and be decreasing.
21
22Index() --> A null interval with no elements.
23Index(n) --> make an Index on [0..n-1]
24Index(a,b) --> make an Index on [a..b]
25Index(a,b,s) --> make an Index on [a..b] with stride s
26
27Example1:
28--------
29Index I(10); // Index on [0..9]
30Index Low(5); // Index on [0..4]
31Index High(5,9); // Index on [5..9]
32Index IOdd(1,9,2); // Index on [1..9] stride 2
33Index IEven(0,9,2); // Index on [0..9] stride 2
34
35NDIndex<1> domain;
36domain[0] = I;
37FieldLayout<1> layout(domain) // Construct the layout
38Field<double, Dim> A(layout) // Construct the field
39
40A = 0;
41
42cout << A << endl;
43
44A[Low] = 1.0;
45
46cout << A << endl;
47
48A[High] = 2.0;
49
50cout << A << endl;
51
52A[IEven] -= 1.0;
53
54cout << A << endl;
55
56A[IOdd] -= 1.0;
57
58cout << A << endl;
59
60Output::
61--------
620 0 0 0 0 0 0 0 0 0
631 1 1 1 1 0 0 0 0 0
641 1 1 1 1 2 2 2 2 2
650 1 0 1 0 2 1 2 1 2
660 0 0 0 0 1 1 1 1 1
67
68The same functionality can be reproduced without constucting additional
69Index objects by making use of the binary operations.
70
71--------
72Index I(10); // Index on [0..9]
73NDIndex<1> domain; //
74FieldLayout<1> layout(domain) // Construct the layout
75Field<double, Dim> A(layout) // Construct the field
76
77A = 0;
78
79cout << A << endl;
80
81A[I-5] = 1.0;
82
83cout << A << endl;
84
85A[I+5] = 2.0;
86
87cout << A << endl;
88
89A[I*2] -= 1.0;
90
91cout << A << endl;
92
93A[I*2+1] -= 1.0;
94
95cout << A << endl;
96
97Output::
98--------
990 0 0 0 0 0 0 0 0 0
1001 1 1 1 1 0 0 0 0 0
1011 1 1 1 1 2 2 2 2 2
1020 1 0 1 0 2 1 2 1 2
1030 0 0 0 0 1 1 1 1 1
104
105
106Given an Index I(a,n,s), and an integer j you can do the following:
107
108I+j : a+j+i*s for i in [0..n-1]
109j-I : j-a-i*s
110j*I : j*a + i*j*s
111I/j : a/j + i*s/j
112
113j/I we don't do because it is not a uniform stride, and we don't
114allow strides that are fractions.
115
116When performing these arithmetic operations the Index keeps
117track of some information about a base index so we can deduce
118what to do in array expressions. For example if you want to do
119
120 A[I] = B[I+1];
121
122you need to be able to be sure that you used the same Index on both
123sides, and you need to be able to tell that the result of
124operator+(const Index&,int) on the right is offset 1 from I.
125
126For the first requirement it keeps track of a pointer to the base
127Index that it came from.
128
129The offset stuff is slightly trickier, as it does a number of
130things to be sure it can do fairly arbitrary intersections and so on
131efficiently.
132
133Each Index has a "domain" of contiguous increasing integers.
134Each Index has a "range" of integers with constant stride.
135If i is in the domain [a,b], the range is f+i*s, where f+a*s
136is the "first" element of the range and f+b*s is the "last".
137Because we don't allow fractional strides, the range has no
138repeated elements.
139
140This "ordering" is imposed so that expressions like
141
142 A[alpha+beta*I] = B[gamma + delta*I];
143
144have a clearly defined meaning:
145
146 for (i=a; i<=b; i++)
147 A[alpha+beta*(f+i*s)] = B[gamma+delta*(f+i*s)];
148
149In practice we may choose whatever order we like for efficiency,
150but the definition remains the same.
151
152This is evaluated as follows.
153
154alpha + beta*I is evaluated to give I1 with the same domain as
155I and the range given by f1 = alpha+beta*f, and s1=beta*s.
156gamma + delta*I gives I2, with f2=gamma+delta*f, s2=delta*s.
157Then we evaluate A[I1] = B[I2], which is equivalent to
158
159 for (i=a; i<=b; i++)
160 A[f1+i*s1] = B[f2+i*s2];
161
162With what has been defined up to now, we can always choose a=0.
163The following will relax that.
164
165All of this would be quite straightforward in serial, the fun
166begins in parallel. Indexes are not parallel objects, but they
167have operations defined for them for making regular section
168transfer calculations easier: intersect and plugBase.
169
170The idea is that we represent the part of A and B that reside on
171various processors with an Index. That index is the local domain
172of the array.
173
174So, when we want to find what part of A[I1] is on processor p we do:
175
176 Index LocalDestinationRange = I1.intersect(A.localDomain(p));
177
178the Index LocalDestinationRange has a range which is those elments
179in the range of I1 that are on processor p. (We restrict the possible
180domains to contiguous integers -- no fair putting the odds on one
181processor and the evens on another for now!) That tells us where
182data is going to end up on this processor. The mapping between the
183Index's domain and its range is preserved under this operation.
184
185From that we need to find where those elements come from.
186
187XXjr Index LocalSourceRange = I2.plugBase(LocalSourceRange);
188 Index LocalSourceRange = I2.plugBase(LocalDestinationRange);
189
190XXjr This plugs the domain of LocalSourceRange into I2, to get where
191XXjr in I2 the elements will be coming from.
192This plugs the domain of LocalDestinationRange into I2, to get where
193in I2 the elements will be coming from.
194
195Then for every candidate other processor pp, we intersect LocalSourceRange
196with that processor's domain for B and we have what needs to be
197comminicated from that processor.
198
199 Index RemoteSourceRange = LocalSourceRange.intersect(B.localDomain(pp));
200
201Doing this operation for all the candidate pp's produces the get
202schedule.
203
204Finding the put schedule is very similar. Start by finding what parts
205of I2 are on this processor:
206
207 Index LocalSourceRange = I2.intersect(B.localDomain(p));
208
209Plug the domain of that into I1 to find where they're going:
210
211 Index LocalDestRange = I1.plugBase(LocalSourceRange);
212
213Intersect that with all the candidate processors pp to find what
214processor to send them to:
215
216 Index RemoteDestRange = LocalDestRange.intersect(A.localDomain(pp));
217
218One last plugBase puts that back in terms of the range of B:
219
220 Index RemoteSourceRange = LocalSourceRange.plugBase(RemoteDestRange);
221
222And that is how the put and get schedules are calculated.
223
224***********************************************************************/
225
226
227// include files
228#include "PETE/IpplExpressions.h"
229#include <iostream>
230
231// forward declarations
232class Index;
233std::ostream& operator<<(std::ostream& out, const Index& I);
234
235
236class Index : public PETE_Expr<Index>
237{
238
239public:
241 {
242 public:
243
244 iterator() : Current(0) , Stride(0) {}
245 iterator(int current, int stride=1) : Current(current), Stride(stride) {}
246
247 int operator*() { return Current ; }
249 {
250 iterator tmp = *this;
251 Current -= Stride; // Post decrement
252 return tmp;
253 }
255 {
256 Current -= Stride;
257 return (*this);
258 }
260 {
261 iterator tmp = *this;
262 Current += Stride; // Post increment
263 return tmp;
264 }
266 {
267 Current += Stride;
268 return (*this);
269 }
271 {
272 Current += (Stride * i);
273 return *this;
274 }
276 {
277 Current -= (Stride * i);
278 return *this;
279 }
280 iterator operator+(int i) const
281 {
282 return iterator(Current+i*Stride,Stride);
283 }
284 iterator operator-(int i) const
285 {
286 return iterator(Current-i*Stride,Stride);
287 }
288 int operator[](int i) const
289 {
290 return Current + i * Stride;
291 }
292 bool operator==(const iterator &y) const
293 {
294 return (Current == y.Current) && (Stride == y.Stride);
295 }
296 bool operator<(const iterator &y) const
297 {
298 return (Current < y.Current)||
299 ((Current==y.Current)&&(Stride<y.Stride));
300 }
301 bool operator!=(const iterator &y) const { return !((*this) == y); }
302 bool operator> (const iterator &y) const { return y < (*this); }
303 bool operator<=(const iterator &y) const { return !(y < (*this)); }
304 bool operator>=(const iterator &y) const { return !((*this) < y); }
305 private:
306
309 };
310
311 class cursor : public PETE_Expr<cursor>
312 {
313 private:
316 int First;
317 unsigned Dim;
318 const Index* I;
319 public:
321 cursor(const Index& i)
322 : Current(i.first()),
323 Stride(i.stride()),
324 First(i.first()),
325 Dim(0),
326 I(&i)
327 {
328 }
329
330 int operator*() const { return Current; }
331 int offset() const { return Current; }
332 int offset(int i) const
333 {
334 return Current +
335 ( Dim==0 ? i*Stride : 0 );
336 }
337 int offset(int i, int j) const
338 {
339 return Current +
340 ( Dim==0 ? i*Stride : 0 ) +
341 ( Dim==1 ? j*Stride : 0 );
342 }
343 int offset(int i, int j, int k) const
344 {
345 return Current +
346 ( Dim==0 ? i*Stride : 0 ) +
347 ( Dim==1 ? j*Stride : 0 ) +
348 ( Dim==2 ? k*Stride : 0 );
349 }
350 void step(unsigned d)
351 {
352 if ( d==Dim )
353 Current += Stride;
354 }
355 void rewind(unsigned d)
356 {
357 if ( d==Dim )
358 Current = First;
359 }
360 bool plugBase(const Index& i, unsigned d=0)
361 {
362 Index plugged( I->plugBase(i) );
363 Current = First = plugged.first();
364 Stride = plugged.stride();
365 Dim = d;
366 return true;
367 }
368 int id() const { return I->id(); }
369
370 // PETE interface.
371 enum { IsExpr = 1 };
373 typedef int PETE_Return_t;
374 cursor MakeExpression() const { return *this; }
375 };
376
377 // Member functions. Make almost all of these inline for efficiency.
378
379 Index(); // Null range.
380 inline Index(unsigned n); // [0..n-1]
381 inline Index(int f, int l); // [f..l]
382 inline Index(int f, int l, int s); // First to Last using Step.
383
384 ~Index() {}; // Don't need to do anything.
385 int id() const { return Base; }
386
387 inline int min() const; // the smallest element.
388 inline int max() const; // the largest element.
389 inline unsigned int length() const; // the number of elems.
390 inline int stride() const; // the stride.
391 inline int first() const; // the first element.
392 inline int last() const; // the last element.
393 inline bool empty() const; // is it empty?
394 inline int getBase() const; // the id from the base index
395
396 // Additive operations.
397 friend inline Index operator+(const Index&,int);
398 friend inline Index operator+(int,const Index&);
399 friend inline Index operator-(const Index&,int);
400 friend inline Index operator-(int,const Index&);
401
402 // Multipplicative operations.
403 friend inline Index operator-(const Index&);
404 friend inline Index operator*(const Index&,int);
405 friend inline Index operator*(int,const Index&);
406 friend inline Index operator/(const Index&,int);
407
408 // Intersect with another Index.
409 Index intersect(const Index &) const;
410
411 // Plug the base range of one into another.
412 inline Index plugBase(const Index &) const;
413
414 // Test to see if two indexes are from the same base.
415 inline bool sameBase(const Index&) const;
416
417 // Test to see if there is any overlap between two Indexes.
418 inline bool touches (const Index&a) const;
419 // Test to see if one contains another (endpoints only)
420 inline bool contains(const Index&a) const;
421 // Test to see if one contains another (all points)
422 inline bool containsAllPoints(const Index &b) const;
423 // Split one into two.
424 inline bool split(Index& l, Index& r) const;
425 // Split index into two with a ratio between 0 and 1.
426 inline bool split(Index& l, Index& r, double a) const;
427
428 // iterator begin
430 // iterator end
432
433 // An operator< so we can impose some sort of ordering.
434 bool operator<(const Index& r) const
435 {
436 return ( (Length< r.Length) ||
437 ( (Length==r.Length) && ( (First<r.First) ||
438 ( (First==r.First) && (Length>0) && (Stride<r.Stride) ) ) ) );
439 }
440 // Test for equality.
441 bool operator==(const Index& r) const
442 {
443 return (Length==r.Length) && (First==r.First) && (Stride==r.Stride);
444 }
445
446 static void findPut(const Index&,const Index&, const Index&,Index&,Index&);
447
448 // put data into a message to send to another node
450 int dbuf[3];
451 int *d = dbuf;
452 d[0] = first();
453 d[1] = stride();
454 d[2] = length();
455 m.put(d, d + 3);
456 return m;
457 }
458
459 // get data out from a message
461 int dbuf[3];
462 int *d = dbuf;
463 m.get(d);
464 *this = Index(d[0], d[0] + (d[2] - 1)*d[1], d[1]);
465 return m;
466 }
467
468 // PETE interface.
470 cursor MakeExpression() const { return cursor(*this); }
471
472private:
473
474 // Here is the first element, the number of elements and the stride.
475 int First;
477 unsigned Length;
478
479 // Here we store the first element of the base index.
480 // This gets updated whenever we do index or set operations
481 // so we can do inverses quickly and easily.
482 unsigned BaseFirst;
483
484 // Keep id for the base so we can tell when two
485 // indexes come from the same base.
486 int Base;
487
488 // Make an Index that interally counts the other direction.
489 inline Index reverse() const;
490
491 // Construct with a given base. This is private because
492 // the interface shouldn't depend on how this is done.
493 inline Index(int m, int a, const Index &b);
494 inline Index(int f, int s, const Index *b);
495
496 // Do a general intersect if the strides are not both 1.
497 Index general_intersect(const Index&) const;
498
499 // Provide a way to not initialize on construction.
502};
503
505
506#include "Index/IndexInlines.h"
507
509
510#endif // INDEX_H
511
512/***************************************************************************
513 * $RCSfile: Index.h,v $ $Author: adelmann $
514 * $Revision: 1.1.1.1 $ $Date: 2003/01/23 07:40:27 $
515 * IPPL_VERSION_ID: $Id: Index.h,v 1.1.1.1 2003/01/23 07:40:27 adelmann Exp $
516 ***************************************************************************/
std::ostream & operator<<(std::ostream &out, const Index &I)
Definition: Index.cpp:38
std::complex< double > a
std::string::iterator iterator
Definition: MSLang.h:16
Definition: Index.h:237
iterator end()
Definition: Index.h:431
bool split(Index &l, Index &r) const
Definition: IndexInlines.h:285
int getBase() const
Definition: IndexInlines.h:151
int stride() const
Definition: IndexInlines.h:121
int Base
Definition: Index.h:486
Message & putMessage(Message &m) const
Definition: Index.h:449
Message & getMessage(Message &m)
Definition: Index.h:460
friend Index operator+(const Index &, int)
Definition: IndexInlines.h:162
unsigned BaseFirst
Definition: Index.h:482
bool empty() const
Definition: IndexInlines.h:126
int last() const
Definition: IndexInlines.h:136
Index intersect(const Index &) const
Definition: Index.cpp:108
int max() const
Definition: IndexInlines.h:146
bool operator<(const Index &r) const
Definition: Index.h:434
~Index()
Definition: Index.h:384
cursor MakeExpression() const
Definition: Index.h:470
int Stride
Definition: Index.h:476
unsigned Length
Definition: Index.h:477
friend Index operator/(const Index &, int)
Definition: IndexInlines.h:197
bool containsAllPoints(const Index &b) const
Definition: IndexInlines.h:253
Index plugBase(const Index &) const
Definition: IndexInlines.h:215
int First
Definition: Index.h:475
Index reverse() const
Definition: IndexInlines.h:228
friend Index operator-(const Index &, int)
Definition: IndexInlines.h:172
bool contains(const Index &a) const
Definition: IndexInlines.h:248
bool touches(const Index &a) const
Definition: IndexInlines.h:242
Index(DontInitialize)
Definition: Index.h:501
cursor PETE_Expr_t
Definition: Index.h:469
bool operator==(const Index &r) const
Definition: Index.h:441
iterator begin()
Definition: Index.h:429
friend Index operator*(const Index &, int)
Definition: IndexInlines.h:187
static void findPut(const Index &, const Index &, const Index &, Index &, Index &)
int min() const
Definition: IndexInlines.h:141
bool sameBase(const Index &) const
Definition: IndexInlines.h:208
unsigned int length() const
Definition: IndexInlines.h:131
int id() const
Definition: Index.h:385
Index general_intersect(const Index &) const
Definition: Index.cpp:173
int first() const
Definition: IndexInlines.h:116
int operator*()
Definition: Index.h:247
iterator operator--(int)
Definition: Index.h:248
bool operator>(const iterator &y) const
Definition: Index.h:302
iterator operator++(int)
Definition: Index.h:259
iterator & operator-=(int i)
Definition: Index.h:275
bool operator<=(const iterator &y) const
Definition: Index.h:303
bool operator>=(const iterator &y) const
Definition: Index.h:304
int operator[](int i) const
Definition: Index.h:288
bool operator==(const iterator &y) const
Definition: Index.h:292
iterator & operator++()
Definition: Index.h:265
iterator & operator--()
Definition: Index.h:254
iterator operator+(int i) const
Definition: Index.h:280
bool operator!=(const iterator &y) const
Definition: Index.h:301
iterator operator-(int i) const
Definition: Index.h:284
iterator(int current, int stride=1)
Definition: Index.h:245
iterator & operator+=(int i)
Definition: Index.h:270
bool operator<(const iterator &y) const
Definition: Index.h:296
int PETE_Return_t
Definition: Index.h:373
int id() const
Definition: Index.h:368
cursor(const Index &i)
Definition: Index.h:321
int offset(int i) const
Definition: Index.h:332
int First
Definition: Index.h:316
int Stride
Definition: Index.h:315
bool plugBase(const Index &i, unsigned d=0)
Definition: Index.h:360
void step(unsigned d)
Definition: Index.h:350
int offset(int i, int j) const
Definition: Index.h:337
int operator*() const
Definition: Index.h:330
const Index * I
Definition: Index.h:318
unsigned Dim
Definition: Index.h:317
void rewind(unsigned d)
Definition: Index.h:355
int Current
Definition: Index.h:314
cursor MakeExpression() const
Definition: Index.h:374
cursor PETE_Expr_t
Definition: Index.h:372
int offset(int i, int j, int k) const
Definition: Index.h:343
int offset() const
Definition: Index.h:331
Message & put(const T &val)
Definition: Message.h:406
Message & get(const T &cval)
Definition: Message.h:476
Definition: PETE.h:77