OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
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 #ifdef IPPL_PURIFY
331  cursor(const cursor &model)
332  : Current(model.Current), Stride(model.Stride),
333  First(model.First), Dim(model.Dim), I(model.I) {}
334  cursor& operator=(const cursor &rhs)
335  {
336  Current = rhs.Current;
337  Stride = rhs.Stride;
338  First = rhs.First;
339  Dim = rhs.Dim;
340  I = rhs.I;
341  return *this;
342  }
343 #endif
344 
345  int operator*() const { return Current; }
346  int offset() const { return Current; }
347  int offset(int i) const
348  {
349  return Current +
350  ( Dim==0 ? i*Stride : 0 );
351  }
352  int offset(int i, int j) const
353  {
354  return Current +
355  ( Dim==0 ? i*Stride : 0 ) +
356  ( Dim==1 ? j*Stride : 0 );
357  }
358  int offset(int i, int j, int k) const
359  {
360  return Current +
361  ( Dim==0 ? i*Stride : 0 ) +
362  ( Dim==1 ? j*Stride : 0 ) +
363  ( Dim==2 ? k*Stride : 0 );
364  }
365  void step(unsigned d)
366  {
367  if ( d==Dim )
368  Current += Stride;
369  }
370  void rewind(unsigned d)
371  {
372  if ( d==Dim )
373  Current = First;
374  }
375  bool plugBase(const Index& i, unsigned d=0)
376  {
377  Index plugged( I->plugBase(i) );
378  Current = First = plugged.first();
379  Stride = plugged.stride();
380  Dim = d;
381  return true;
382  }
383  int id() const { return I->id(); }
384 
385  // PETE interface.
386  enum { IsExpr = 1 };
388  typedef int PETE_Return_t;
389  cursor MakeExpression() const { return *this; }
390  };
391 
392  // Member functions. Make almost all of these inline for efficiency.
393 
394  Index(); // Null range.
395  inline Index(unsigned n); // [0..n-1]
396  inline Index(int f, int l); // [f..l]
397  inline Index(int f, int l, int s); // First to Last using Step.
398 
399  ~Index() {}; // Don't need to do anything.
400  int id() const { return Base; }
401 
402  inline int min() const; // the smallest element.
403  inline int max() const; // the largest element.
404  inline unsigned int length() const; // the number of elems.
405  inline int stride() const; // the stride.
406  inline int first() const; // the first element.
407  inline int last() const; // the last element.
408  inline bool empty() const; // is it empty?
409  inline int getBase() const; // the id from the base index
410 
411  // Additive operations.
412  friend inline Index operator+(const Index&,int);
413  friend inline Index operator+(int,const Index&);
414  friend inline Index operator-(const Index&,int);
415  friend inline Index operator-(int,const Index&);
416 
417  // Multipplicative operations.
418  friend inline Index operator-(const Index&);
419  friend inline Index operator*(const Index&,int);
420  friend inline Index operator*(int,const Index&);
421  friend inline Index operator/(const Index&,int);
422 
423  // Intersect with another Index.
424  Index intersect(const Index &) const;
425 
426  // Plug the base range of one into another.
427  inline Index plugBase(const Index &) const;
428 
429  // Test to see if two indexes are from the same base.
430  inline bool sameBase(const Index&) const;
431 
432  // Test to see if there is any overlap between two Indexes.
433  inline bool touches (const Index&a) const;
434  // Test to see if one contains another (endpoints only)
435  inline bool contains(const Index&a) const;
436  // Test to see if one contains another (all points)
437  inline bool containsAllPoints(const Index &b) const;
438  // Split one into two.
439  inline bool split(Index& l, Index& r) const;
440  // Split index into two with a ratio between 0 and 1.
441  inline bool split(Index& l, Index& r, double a) const;
442 
443  // iterator begin
445  // iterator end
447 
448  // An operator< so we can impose some sort of ordering.
449  bool operator<(const Index& r) const
450  {
451  return ( (Length< r.Length) ||
452  ( (Length==r.Length) && ( (First<r.First) ||
453  ( (First==r.First) && (Length>0) && (Stride<r.Stride) ) ) ) );
454  }
455  // Test for equality.
456  bool operator==(const Index& r) const
457  {
458  return (Length==r.Length) && (First==r.First) && (Stride==r.Stride);
459  }
460 
461  static void findPut(const Index&,const Index&, const Index&,Index&,Index&);
462 
463  // put data into a message to send to another node
465  int dbuf[3];
466  int *d = dbuf;
467  d[0] = first();
468  d[1] = stride();
469  d[2] = length();
470  m.put(d, d + 3);
471  return m;
472  }
473 
474  // get data out from a message
476  int dbuf[3];
477  int *d = dbuf;
478  m.get(d);
479  *this = Index(d[0], d[0] + (d[2] - 1)*d[1], d[1]);
480  return m;
481  }
482 
483  // PETE interface.
485  cursor MakeExpression() const { return cursor(*this); }
486 
487 private:
488 
489  // Here is the first element, the number of elements and the stride.
490  int First;
491  int Stride;
492  unsigned Length;
493 
494  // Here we store the first element of the base index.
495  // This gets updated whenever we do index or set operations
496  // so we can do inverses quickly and easily.
497  unsigned BaseFirst;
498 
499  // Keep id for the base so we can tell when two
500  // indexes come from the same base.
501  int Base;
502 
503  // Make an Index that interally counts the other direction.
504  inline Index reverse() const;
505 
506  // Construct with a given base. This is private because
507  // the interface shouldn't depend on how this is done.
508  inline Index(int m, int a, const Index &b);
509  inline Index(int f, int s, const Index *b);
510 
511  // Do a general intersect if the strides are not both 1.
512  Index general_intersect(const Index&) const;
513 
514  // Provide a way to not initialize on construction.
515  class DontInitialize {};
517 };
518 
520 
521 #include "Index/IndexInlines.h"
522 
524 
525 #endif // INDEX_H
526 
527 /***************************************************************************
528  * $RCSfile: Index.h,v $ $Author: adelmann $
529  * $Revision: 1.1.1.1 $ $Date: 2003/01/23 07:40:27 $
530  * IPPL_VERSION_ID: $Id: Index.h,v 1.1.1.1 2003/01/23 07:40:27 adelmann Exp $
531  ***************************************************************************/
cursor(const Index &i)
Definition: Index.h:321
cursor PETE_Expr_t
Definition: Index.h:387
iterator operator++(int)
Definition: Index.h:259
std::ostream & operator<<(std::ostream &os, const Attribute &attr)
Definition: Attribute.cpp:167
bool operator>=(const iterator &y) const
Definition: Index.h:304
Definition: PETE.h:80
Index intersect(const Index &) const
Definition: Index.cpp:108
cursor MakeExpression() const
Definition: Index.h:485
bool containsAllPoints(const Index &b) const
Definition: IndexInlines.h:253
friend Index operator-(const Index &, int)
Definition: IndexInlines.h:172
cursor MakeExpression() const
Definition: Index.h:389
int Stride
Definition: Index.h:315
int offset(int i) const
Definition: Index.h:347
int Base
Definition: Index.h:501
Index(DontInitialize)
Definition: Index.h:516
Index reverse() const
Definition: IndexInlines.h:228
Message & getMessage(Message &m)
Definition: Index.h:475
int Current
Definition: Index.h:314
Message & putMessage(Message &m) const
Definition: Index.h:464
bool split(Index &l, Index &r) const
Definition: IndexInlines.h:285
bool operator<(const iterator &y) const
Definition: Index.h:296
bool operator<=(const iterator &y) const
Definition: Index.h:303
iterator end()
Definition: Index.h:446
~Index()
Definition: Index.h:399
friend Index operator+(const Index &, int)
Definition: IndexInlines.h:162
unsigned int length() const
Definition: IndexInlines.h:131
int First
Definition: Index.h:316
bool sameBase(const Index &) const
Definition: IndexInlines.h:208
bool operator<(const Index &r) const
Definition: Index.h:449
int offset(int i, int j) const
Definition: Index.h:352
int getBase() const
Definition: IndexInlines.h:151
unsigned Length
Definition: Index.h:492
int First
Definition: Index.h:490
Definition: Index.h:236
int last() const
Definition: IndexInlines.h:136
iterator(int current, int stride=1)
Definition: Index.h:245
iterator & operator+=(int i)
Definition: Index.h:270
static void findPut(const Index &, const Index &, const Index &, Index &, Index &)
iterator & operator-=(int i)
Definition: Index.h:275
int Stride
Definition: Index.h:491
iterator & operator--()
Definition: Index.h:254
iterator begin()
Definition: Index.h:444
int offset(int i, int j, int k) const
Definition: Index.h:358
friend Index operator/(const Index &, int)
Definition: IndexInlines.h:197
void step(unsigned d)
Definition: Index.h:365
Index general_intersect(const Index &) const
Definition: Index.cpp:190
int id() const
Definition: Index.h:383
bool operator==(const iterator &y) const
Definition: Index.h:292
Message & get(const T &cval)
Definition: Message.h:484
int id() const
Definition: Index.h:400
bool touches(const Index &a) const
Definition: IndexInlines.h:242
iterator operator+(int i) const
Definition: Index.h:280
int PETE_Return_t
Definition: Index.h:388
Message & put(const T &val)
Definition: Message.h:414
int operator*()
Definition: Index.h:247
int offset() const
Definition: Index.h:346
bool operator>(const iterator &y) const
Definition: Index.h:302
unsigned Dim
Definition: Index.h:317
bool operator==(const Index &r) const
Definition: Index.h:456
int max() const
Definition: IndexInlines.h:146
Index plugBase(const Index &) const
Definition: IndexInlines.h:215
bool empty() const
Definition: IndexInlines.h:126
int stride() const
Definition: IndexInlines.h:121
int first() const
Definition: IndexInlines.h:116
void rewind(unsigned d)
Definition: Index.h:370
friend Index operator*(const Index &, int)
Definition: IndexInlines.h:187
std::string::iterator iterator
Definition: MSLang.h:16
bool plugBase(const Index &i, unsigned d=0)
Definition: Index.h:375
int operator*() const
Definition: Index.h:345
int operator[](int i) const
Definition: Index.h:288
cursor PETE_Expr_t
Definition: Index.h:484
iterator & operator++()
Definition: Index.h:265
bool contains(const Index &a) const
Definition: IndexInlines.h:248
bool operator!=(const iterator &y) const
Definition: Index.h:301
int min() const
Definition: IndexInlines.h:141
iterator operator-(int i) const
Definition: Index.h:284
iterator operator--(int)
Definition: Index.h:248
unsigned BaseFirst
Definition: Index.h:497
const Index * I
Definition: Index.h:318