OPAL (Object Oriented Parallel Accelerator Library)  2021.1.99
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 
16 Define a slice in an array.
17 
18 This essentially defines a list of evenly spaced numbers.
19 Most commonly this list will be increasing (positive stride)
20 but it can also have negative stride and be decreasing.
21 
22 Index() --> A null interval with no elements.
23 Index(n) --> make an Index on [0..n-1]
24 Index(a,b) --> make an Index on [a..b]
25 Index(a,b,s) --> make an Index on [a..b] with stride s
26 
27 Example1:
28 --------
29 Index I(10); // Index on [0..9]
30 Index Low(5); // Index on [0..4]
31 Index High(5,9); // Index on [5..9]
32 Index IOdd(1,9,2); // Index on [1..9] stride 2
33 Index IEven(0,9,2); // Index on [0..9] stride 2
34 
35 NDIndex<1> domain;
36 domain[0] = I;
37 FieldLayout<1> layout(domain) // Construct the layout
38 Field<double, Dim> A(layout) // Construct the field
39 
40 A = 0;
41 
42 cout << A << endl;
43 
44 A[Low] = 1.0;
45 
46 cout << A << endl;
47 
48 A[High] = 2.0;
49 
50 cout << A << endl;
51 
52 A[IEven] -= 1.0;
53 
54 cout << A << endl;
55 
56 A[IOdd] -= 1.0;
57 
58 cout << A << endl;
59 
60 Output::
61 --------
62 0 0 0 0 0 0 0 0 0 0
63 1 1 1 1 1 0 0 0 0 0
64 1 1 1 1 1 2 2 2 2 2
65 0 1 0 1 0 2 1 2 1 2
66 0 0 0 0 0 1 1 1 1 1
67 
68 The same functionality can be reproduced without constucting additional
69 Index objects by making use of the binary operations.
70 
71 --------
72 Index I(10); // Index on [0..9]
73 NDIndex<1> domain; //
74 FieldLayout<1> layout(domain) // Construct the layout
75 Field<double, Dim> A(layout) // Construct the field
76 
77 A = 0;
78 
79 cout << A << endl;
80 
81 A[I-5] = 1.0;
82 
83 cout << A << endl;
84 
85 A[I+5] = 2.0;
86 
87 cout << A << endl;
88 
89 A[I*2] -= 1.0;
90 
91 cout << A << endl;
92 
93 A[I*2+1] -= 1.0;
94 
95 cout << A << endl;
96 
97 Output::
98 --------
99 0 0 0 0 0 0 0 0 0 0
100 1 1 1 1 1 0 0 0 0 0
101 1 1 1 1 1 2 2 2 2 2
102 0 1 0 1 0 2 1 2 1 2
103 0 0 0 0 0 1 1 1 1 1
104 
105 
106 Given an Index I(a,n,s), and an integer j you can do the following:
107 
108 I+j : a+j+i*s for i in [0..n-1]
109 j-I : j-a-i*s
110 j*I : j*a + i*j*s
111 I/j : a/j + i*s/j
112 
113 j/I we don't do because it is not a uniform stride, and we don't
114 allow strides that are fractions.
115 
116 When performing these arithmetic operations the Index keeps
117 track of some information about a base index so we can deduce
118 what to do in array expressions. For example if you want to do
119 
120  A[I] = B[I+1];
121 
122 you need to be able to be sure that you used the same Index on both
123 sides, and you need to be able to tell that the result of
124 operator+(const Index&,int) on the right is offset 1 from I.
125 
126 For the first requirement it keeps track of a pointer to the base
127 Index that it came from.
128 
129 The offset stuff is slightly trickier, as it does a number of
130 things to be sure it can do fairly arbitrary intersections and so on
131 efficiently.
132 
133 Each Index has a "domain" of contiguous increasing integers.
134 Each Index has a "range" of integers with constant stride.
135 If i is in the domain [a,b], the range is f+i*s, where f+a*s
136 is the "first" element of the range and f+b*s is the "last".
137 Because we don't allow fractional strides, the range has no
138 repeated elements.
139 
140 This "ordering" is imposed so that expressions like
141 
142  A[alpha+beta*I] = B[gamma + delta*I];
143 
144 have 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 
149 In practice we may choose whatever order we like for efficiency,
150 but the definition remains the same.
151 
152 This is evaluated as follows.
153 
154 alpha + beta*I is evaluated to give I1 with the same domain as
155 I and the range given by f1 = alpha+beta*f, and s1=beta*s.
156 gamma + delta*I gives I2, with f2=gamma+delta*f, s2=delta*s.
157 Then 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 
162 With what has been defined up to now, we can always choose a=0.
163 The following will relax that.
164 
165 All of this would be quite straightforward in serial, the fun
166 begins in parallel. Indexes are not parallel objects, but they
167 have operations defined for them for making regular section
168 transfer calculations easier: intersect and plugBase.
169 
170 The idea is that we represent the part of A and B that reside on
171 various processors with an Index. That index is the local domain
172 of the array.
173 
174 So, 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 
178 the Index LocalDestinationRange has a range which is those elments
179 in the range of I1 that are on processor p. (We restrict the possible
180 domains to contiguous integers -- no fair putting the odds on one
181 processor and the evens on another for now!) That tells us where
182 data is going to end up on this processor. The mapping between the
183 Index's domain and its range is preserved under this operation.
184 
185 From that we need to find where those elements come from.
186 
187 XXjr Index LocalSourceRange = I2.plugBase(LocalSourceRange);
188  Index LocalSourceRange = I2.plugBase(LocalDestinationRange);
189 
190 XXjr This plugs the domain of LocalSourceRange into I2, to get where
191 XXjr in I2 the elements will be coming from.
192 This plugs the domain of LocalDestinationRange into I2, to get where
193 in I2 the elements will be coming from.
194 
195 Then for every candidate other processor pp, we intersect LocalSourceRange
196 with that processor's domain for B and we have what needs to be
197 comminicated from that processor.
198 
199  Index RemoteSourceRange = LocalSourceRange.intersect(B.localDomain(pp));
200 
201 Doing this operation for all the candidate pp's produces the get
202 schedule.
203 
204 Finding the put schedule is very similar. Start by finding what parts
205 of I2 are on this processor:
206 
207  Index LocalSourceRange = I2.intersect(B.localDomain(p));
208 
209 Plug the domain of that into I1 to find where they're going:
210 
211  Index LocalDestRange = I1.plugBase(LocalSourceRange);
212 
213 Intersect that with all the candidate processors pp to find what
214 processor to send them to:
215 
216  Index RemoteDestRange = LocalDestRange.intersect(A.localDomain(pp));
217 
218 One last plugBase puts that back in terms of the range of B:
219 
220  Index RemoteSourceRange = LocalSourceRange.plugBase(RemoteDestRange);
221 
222 And 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
232 class Index;
233 std::ostream& operator<<(std::ostream& out, const Index& I);
234 
235 
236 class Index : public PETE_Expr<Index>
237 {
238 
239 public:
240  class iterator
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 
307  int Current;
308  int Stride;
309  };
310 
311  class cursor : public PETE_Expr<cursor>
312  {
313  private:
314  int Current;
315  int Stride;
316  int First;
317  unsigned Dim;
318  const Index* I;
319  public:
320  cursor() {}
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 
472 private:
473 
474  // Here is the first element, the number of elements and the stride.
475  int First;
476  int Stride;
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.
500  class DontInitialize {};
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
friend Index operator+(const Index &, int)
Definition: IndexInlines.h:162
unsigned BaseFirst
Definition: Index.h:482
bool empty() const
Definition: IndexInlines.h:126
Message & putMessage(Message &m) const
Definition: Index.h:449
int last() const
Definition: IndexInlines.h:136
Index intersect(const Index &) const
Definition: Index.cpp:108
int max() const
Definition: IndexInlines.h:146
Message & getMessage(Message &m)
Definition: Index.h:460
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
iterator & operator-=(int i)
Definition: Index.h:275
int operator*()
Definition: Index.h:247
iterator & operator--()
Definition: Index.h:254
iterator operator--(int)
Definition: Index.h:248
bool operator>(const iterator &y) const
Definition: Index.h:302
iterator & operator+=(int i)
Definition: Index.h:270
iterator operator++(int)
Definition: Index.h:259
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+(int i) const
Definition: Index.h:280
bool operator!=(const iterator &y) const
Definition: Index.h:301
iterator & operator++()
Definition: Index.h:265
iterator operator-(int i) const
Definition: Index.h:284
iterator(int current, int stride=1)
Definition: Index.h:245
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