src/Index/Index.h

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 /***************************************************************************
00003  *
00004  * The IPPL Framework
00005  * 
00006  *
00007  * Visit http://people.web.psi.ch/adelmann/ for more details
00008  *
00009  ***************************************************************************/
00010 
00011 #ifndef INDEX_H
00012 #define INDEX_H
00013 
00014 /***********************************************************************
00015 
00016 Define a slice in an array.
00017 
00018 This essentially defines a list of evenly spaced numbers.
00019 Most commonly this list will be increasing (positive stride)
00020 but it can also have negative stride and be decreasing.
00021 
00022 Index()      --> A null interval with no elements.
00023 Index(n)     --> make an Index on [0..n-1]
00024 Index(a,b)   --> make an Index on [a..b]
00025 Index(a,b,s) --> make an Index on [a..b] with stride s
00026 
00027 Example1:
00028 --------
00029 Index I(10);                    // Index on [0..9]
00030 Index Low(5);                   // Index on [0..4]
00031 Index High(5,9);                // Index on [5..9]
00032 Index IOdd(1,9,2);              // Index on [1..9] stride 2
00033 Index IEven(0,9,2);             // Index on [0..9] stride 2
00034 
00035 NDIndex<1> domain;
00036 domain[0] = I;
00037 FieldLayout<1> layout(domain)   // Construct the layout
00038 Field<double, Dim> A(layout)    // Construct the field
00039 
00040 A = 0;
00041 
00042 cout << A << endl;
00043 
00044 A[Low] = 1.0;
00045 
00046 cout << A << endl;
00047 
00048 A[High] = 2.0;
00049 
00050 cout << A << endl;
00051 
00052 A[IEven] -= 1.0;
00053 
00054 cout << A << endl;
00055 
00056 A[IOdd] -= 1.0;
00057 
00058 cout << A << endl;
00059 
00060 Output::
00061 --------
00062 0 0 0 0 0 0 0 0 0 0 
00063 1 1 1 1 1 0 0 0 0 0 
00064 1 1 1 1 1 2 2 2 2 2 
00065 0 1 0 1 0 2 1 2 1 2 
00066 0 0 0 0 0 1 1 1 1 1 
00067 
00068 The same functionality can be reproduced without constucting additional
00069 Index objects by making use of the binary operations.
00070 
00071 --------
00072 Index I(10);                    // Index on [0..9]
00073 NDIndex<1> domain;              //
00074 FieldLayout<1> layout(domain)   // Construct the layout
00075 Field<double, Dim> A(layout)    // Construct the field
00076 
00077 A = 0;
00078 
00079 cout << A << endl;
00080 
00081 A[I-5] = 1.0;
00082 
00083 cout << A << endl;
00084 
00085 A[I+5] = 2.0;
00086 
00087 cout << A << endl;
00088 
00089 A[I*2] -= 1.0;
00090 
00091 cout << A << endl;
00092 
00093 A[I*2+1] -= 1.0;
00094 
00095 cout << A << endl;
00096 
00097 Output::
00098 --------
00099 0 0 0 0 0 0 0 0 0 0
00100 1 1 1 1 1 0 0 0 0 0
00101 1 1 1 1 1 2 2 2 2 2
00102 0 1 0 1 0 2 1 2 1 2
00103 0 0 0 0 0 1 1 1 1 1
00104 
00105 
00106 Given an Index I(a,n,s), and an integer j you can do the following:
00107 
00108 I+j  : a+j+i*s        for i in [0..n-1]
00109 j-I  : j-a-i*s        
00110 j*I  : j*a + i*j*s    
00111 I/j  : a/j + i*s/j
00112 
00113 j/I we don't do because it is not a uniform stride, and we don't
00114 allow strides that are fractions.
00115 
00116 When performing these arithmetic operations the Index keeps
00117 track of some information about a base index so we can deduce
00118 what to do in array expressions. For example if you want to do
00119 
00120      A[I] = B[I+1];
00121 
00122 you need to be able to be sure that you used the same Index on both
00123 sides, and you need to be able to tell that the result of 
00124 operator+(const Index&,int) on the right is offset 1 from I.
00125 
00126 For the first requirement it keeps track of a pointer to the base 
00127 Index that it came from.
00128 
00129 The offset stuff is slightly trickier, as it does a number of
00130 things to be sure it can do fairly arbitrary intersections and so on
00131 efficiently.
00132 
00133 Each Index has a "domain" of contiguous increasing integers. 
00134 Each Index has a "range" of integers with constant stride.
00135 If i is in the domain [a,b], the range is f+i*s, where f+a*s 
00136 is the "first" element of the range and f+b*s is the "last".
00137 Because we don't allow fractional strides, the range has no
00138 repeated elements.
00139 
00140 This "ordering" is imposed so that expressions like
00141 
00142    A[alpha+beta*I] = B[gamma + delta*I];
00143 
00144 have a clearly defined meaning:
00145 
00146    for (i=a; i<=b; i++) 
00147       A[alpha+beta*(f+i*s)] = B[gamma+delta*(f+i*s)];
00148 
00149 In practice we may choose whatever order we like for efficiency, 
00150 but the definition remains the same.
00151 
00152 This is evaluated as follows.
00153 
00154 alpha + beta*I is evaluated to give I1 with the same domain as
00155 I and the range given by f1 = alpha+beta*f, and s1=beta*s.
00156 gamma + delta*I gives I2, with f2=gamma+delta*f, s2=delta*s.
00157 Then we evaluate A[I1] = B[I2], which is equivalent to 
00158 
00159    for (i=a; i<=b; i++)
00160       A[f1+i*s1] = B[f2+i*s2];
00161 
00162 With what has been defined up to now, we can always choose a=0.
00163 The following will relax that.
00164 
00165 All of this would be quite straightforward in serial, the fun
00166 begins in parallel. Indexes are not parallel objects, but they
00167 have operations defined for them for making regular section 
00168 transfer calculations easier: intersect and plugBase.
00169 
00170 The idea is that we represent the part of A and B that reside on
00171 various processors with an Index. That index is the local domain 
00172 of the array.
00173 
00174 So, when we want to find what part of A[I1] is on processor p we do:
00175 
00176   Index LocalDestinationRange = I1.intersect(A.localDomain(p));
00177 
00178 the Index LocalDestinationRange has a range which is those elments
00179 in the range of I1 that are on processor p. (We restrict the possible
00180 domains to contiguous integers -- no fair putting the odds on one
00181 processor and the evens on another for now!)  That tells us where 
00182 data is going to end up on this processor. The mapping between the 
00183 Index's domain and its range is preserved under this operation.
00184 
00185 From that we need to find where those elements come from. 
00186 
00187 XXjr  Index LocalSourceRange = I2.plugBase(LocalSourceRange);
00188   Index LocalSourceRange = I2.plugBase(LocalDestinationRange); 
00189 
00190 XXjr This plugs the domain of LocalSourceRange into I2, to get where
00191 XXjr in I2 the elements will be coming from.
00192 This plugs the domain of LocalDestinationRange into I2, to get where
00193 in I2 the elements will be coming from.
00194 
00195 Then for every candidate other processor pp, we intersect LocalSourceRange 
00196 with that processor's domain for B and we have what needs to be
00197 comminicated from that processor.
00198 
00199    Index RemoteSourceRange = LocalSourceRange.intersect(B.localDomain(pp));
00200 
00201 Doing this operation for all the candidate pp's produces the get
00202 schedule.
00203 
00204 Finding the put schedule is very similar. Start by finding what parts
00205 of I2 are on this processor:
00206 
00207    Index LocalSourceRange = I2.intersect(B.localDomain(p));
00208 
00209 Plug the domain of that into I1 to find where they're going:
00210 
00211    Index LocalDestRange = I1.plugBase(LocalSourceRange);
00212 
00213 Intersect that with all the candidate processors pp to find what
00214 processor to send them to:
00215 
00216    Index RemoteDestRange = LocalDestRange.intersect(A.localDomain(pp));
00217 
00218 One last plugBase puts that back in terms of the range of B:
00219 
00220    Index RemoteSourceRange = LocalSourceRange.plugBase(RemoteDestRange);
00221 
00222 And that is how the put and get schedules are calculated.
00223 
00224 ***********************************************************************/
00225 
00226 
00227 // include files
00228 #include "PETE/IpplExpressions.h"
00229 
00230 #ifdef IPPL_USE_STANDARD_HEADERS
00231 #include <iostream>
00232 using namespace std;
00233 #else
00234 #include <iostream.h>
00235 #endif
00236 
00237 // forward declarations
00238 class Index;
00239 ostream& operator<<(ostream& out, const Index& I);
00240 
00241 
00242 class Index : public PETE_Expr<Index>
00243 {
00244 
00245 public:
00246   class iterator
00247   {
00248   public:
00249 
00250     iterator() : Current(0), Stride(0) {}
00251     iterator(int current, int stride=1) : Current(current), Stride(stride) {}
00252 
00253     int operator*() { return Current ; }
00254     iterator operator--(int)
00255     {
00256       iterator tmp = *this;
00257       Current -= Stride;             // Post decrement
00258       return tmp;
00259     }
00260     iterator& operator--()
00261     {
00262       Current -= Stride;
00263       return (*this);
00264     }
00265     iterator operator++(int)
00266     {
00267       iterator tmp = *this;
00268       Current += Stride;              // Post increment
00269       return tmp;
00270     }
00271     iterator& operator++()
00272     {
00273       Current += Stride;
00274       return (*this);
00275     }
00276     iterator& operator+=(int i)
00277     {
00278       Current += (Stride * i);
00279       return *this;
00280     }
00281     iterator& operator-=(int i)
00282     {
00283       Current -= (Stride * i);
00284       return *this;
00285     } 
00286     iterator operator+(int i) const
00287     {
00288       return iterator(Current+i*Stride,Stride);
00289     }
00290     iterator operator-(int i) const
00291     {
00292       return iterator(Current-i*Stride,Stride);
00293     }
00294     int operator[](int i) const
00295     {
00296       return Current + i * Stride;
00297     }
00298     bool operator==(const iterator &y) const 
00299     {
00300       return (Current == y.Current) && (Stride == y.Stride);
00301     }
00302     bool operator<(const iterator &y) const
00303     {
00304       return (Current < y.Current)||
00305         ((Current==y.Current)&&(Stride<y.Stride));
00306     }
00307     bool operator!=(const iterator &y) const { return !((*this) == y); }
00308     bool operator> (const iterator &y) const { return y < (*this); }
00309     bool operator<=(const iterator &y) const { return !(y < (*this)); }
00310     bool operator>=(const iterator &y) const { return !((*this) < y); }
00311   private: 
00312 
00313     int Stride;
00314     int Current;
00315   };
00316  
00317   class cursor : public PETE_Expr<cursor>
00318   {
00319   private:
00320     int Current;
00321     int Stride;
00322     int First;
00323     unsigned Dim;
00324     const Index* I;
00325   public:
00326     cursor() {}
00327     cursor(const Index& i)
00328       : Current(i.first()),
00329         Stride(i.stride()),
00330         First(i.first()),
00331         Dim(0),
00332         I(&i)
00333       {
00334       }
00335 
00336 #ifdef IPPL_PURIFY
00337     cursor(const cursor &model)
00338       : Current(model.Current), Stride(model.Stride),
00339         First(model.First), Dim(model.Dim), I(model.I) {}
00340     cursor& operator=(const cursor &rhs)
00341     {
00342       Current = rhs.Current;
00343       Stride = rhs.Stride;
00344       First = rhs.First;
00345       Dim = rhs.Dim;
00346       I = rhs.I;
00347       return *this;
00348     }
00349 #endif
00350 
00351     int operator*() const { return Current; }
00352     int offset() const { return Current; }
00353     int offset(int i) const
00354       {
00355         return Current +
00356           ( Dim==0 ? i*Stride : 0 );
00357       }
00358     int offset(int i, int j)  const
00359       {
00360         return Current +
00361           ( Dim==0 ? i*Stride : 0 ) +
00362           ( Dim==1 ? j*Stride : 0 );
00363       }
00364     int offset(int i, int j, int k) const
00365       {
00366         return Current +
00367           ( Dim==0 ? i*Stride : 0 ) +
00368           ( Dim==1 ? j*Stride : 0 ) +
00369           ( Dim==2 ? k*Stride : 0 );
00370       }
00371     void step(unsigned d)
00372       {
00373         if ( d==Dim )
00374           Current += Stride;
00375       }
00376     void rewind(unsigned d)
00377       {
00378         if ( d==Dim )
00379           Current = First;
00380       }
00381     bool plugBase(const Index& i, unsigned d=0)
00382       {
00383         Index plugged( I->plugBase(i) );
00384         Current = First = plugged.first();
00385         Stride = plugged.stride();
00386         Dim = d;
00387         return true;
00388       }
00389     int id() const { return I->id(); }
00390 
00391     // PETE interface.
00392     enum { IsExpr = 1 };
00393     typedef cursor PETE_Expr_t;
00394     typedef int PETE_Return_t;
00395     cursor MakeExpression() const { return *this; }
00396   };
00397 
00398   // Member functions.  Make almost all of these inline for efficiency.
00399 
00400   Index();                      // Null range.
00401   inline Index(unsigned n);     // [0..n-1]
00402   inline Index(int f, int l);           // [f..l]
00403   inline Index(int f, int l, int s);    // First to Last using Step.
00404 
00405   ~Index() {};                          // Don't need to do anything.
00406   int id() const { return Base; }
00407 
00408   inline int min() const;               // the smallest element.
00409   inline int max() const;               // the largest element.
00410   inline int length() const;            // the number of elems.
00411   inline int stride() const;            // the stride.
00412   inline int first() const;             // the first element.
00413   inline int last() const;              // the last element.
00414   inline bool empty() const;            // is it empty?
00415   inline int getBase() const;   // the id from the base index
00416 
00417   // Additive operations.
00418   friend inline Index operator+(const Index&,int);
00419   friend inline Index operator+(int,const Index&);
00420   friend inline Index operator-(const Index&,int);
00421   friend inline Index operator-(int,const Index&);
00422 
00423   // Multipplicative operations.
00424   friend inline Index operator-(const Index&);
00425   friend inline Index operator*(const Index&,int);
00426   friend inline Index operator*(int,const Index&);
00427   friend inline Index operator/(const Index&,int);
00428 
00429   // Intersect with another Index.
00430   Index intersect(const Index &) const;
00431 
00432   // Plug the base range of one into another.
00433   inline Index plugBase(const Index &) const; 
00434 
00435   // Test to see if two indexes are from the same base.
00436   inline bool sameBase(const Index&) const;
00437 
00438   // Test to see if there is any overlap between two Indexes.
00439   inline bool touches (const Index&a) const;
00440   // Test to see if one contains another (endpoints only)
00441   inline bool contains(const Index&a) const;
00442   // Test to see if one contains another (all points)
00443   inline bool containsAllPoints(const Index &b) const;
00444   // Split one into two.
00445   inline bool split(Index& l, Index& r) const;
00446   // Split index into two with a ratio between 0 and 1.
00447   inline bool split(Index& l, Index& r, double a) const;
00448 
00449   // iterator begin
00450   iterator begin() { return iterator(First,Stride); }
00451   // iterator end
00452   iterator end() { return iterator(First+Stride*Length,Stride); }
00453 
00454   // An operator< so we can impose some sort of ordering.
00455   bool operator<(const Index& r) const 
00456   {
00457     return ((Length<r.Length) ||
00458             (Length==r.Length) && ((First<r.First) ||
00459                                    (First==r.First) &&
00460                                    ((Length>0)&&(Stride<r.Stride))));
00461   }
00462   // Test for equality.
00463   bool operator==(const Index& r) const
00464   {
00465     return (Length==r.Length) && (First==r.First) && (Stride==r.Stride);
00466   }
00467 
00468   static void findPut(const Index&,const Index&, const Index&,Index&,Index&);
00469 
00470   // put data into a message to send to another node
00471   Message& putMessage(Message& m) const {
00472     int dbuf[3];
00473     int *d = dbuf;
00474     d[0] = first();
00475     d[1] = stride();
00476     d[2] = length();
00477     m.put(d, d + 3);
00478     return m;
00479   }
00480 
00481   // get data out from a message
00482   Message& getMessage(Message& m) {
00483     int dbuf[3];
00484     int *d = dbuf;
00485     m.get(d);
00486     *this = Index(d[0], d[0] + (d[2] - 1)*d[1], d[1]);
00487     return m;
00488   }
00489 
00490   // PETE interface.
00491   typedef cursor PETE_Expr_t;
00492   cursor MakeExpression() const { return cursor(*this); }
00493 
00494 private: 
00495 
00496   // Here is the first element, the number of elements and the stride.
00497   int First;
00498   int Stride;
00499   unsigned Length;
00500   
00501   // Here we store the first element of the base index.
00502   // This gets updated whenever we do index or set operations
00503   // so we can do inverses quickly and easily.
00504   unsigned BaseFirst;
00505    
00506   // Keep id for the base so we can tell when two
00507   // indexes come from the same base.
00508   int Base;
00509 
00510   // Make an Index that interally counts the other direction.
00511   inline Index reverse() const;
00512 
00513   // Construct with a given base. This is private because
00514   // the interface shouldn't depend on how this is done.
00515   inline Index(int m, int a, const Index &b);
00516   inline Index(int f, int s, const Index *b);
00517 
00518   // Do a general intersect if the strides are not both 1.
00519   Index general_intersect(const Index&) const;
00520 
00521   // Provide a way to not initialize on construction. 
00522   class DontInitialize {};
00523   Index(DontInitialize) {}
00524 };
00525 
00527 
00528 #include "Index/IndexInlines.h"
00529 
00531 
00532 #endif // INDEX_H
00533 
00534 /***************************************************************************
00535  * $RCSfile: Index.h,v $   $Author: adelmann $
00536  * $Revision: 1.1.1.1 $   $Date: 2003/01/23 07:40:27 $
00537  * IPPL_VERSION_ID: $Id: Index.h,v 1.1.1.1 2003/01/23 07:40:27 adelmann Exp $ 
00538  ***************************************************************************/

Generated on Mon Jan 16 13:23:48 2006 for IPPL by  doxygen 1.4.6