Tpetra parallel linear algebra  Version of the Day
Tpetra_Distribution2D.hpp
1 // @HEADER
2 // ***********************************************************************
3 //
4 // Tpetra: Templated Linear Algebra Services Package
5 // Copyright (2008) Sandia Corporation
6 //
7 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
8 // the U.S. Government retains certain rights in this software.
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 Michael A. Heroux (maherou@sandia.gov)
38 //
39 // ************************************************************************
40 // @HEADER
41 
42 // 2D matrix distribution
43 // Assumes square matrix
44 // Karen Devine, SNL
45 //
46 
47 #ifndef __TPETRA_DISTRIBUTION2D_HPP
48 #define __TPETRA_DISTRIBUTION2D_HPP
49 
50 namespace Tpetra
51 {
52 
54 template <typename gno_t, typename scalar_t>
55 class Distribution2D : public Distribution<gno_t,scalar_t> {
56 // Processors will be laid out logically first down columns then
57 // across rows. For example, assume np = 8, npRows=2, npCols=4.
58 // Then the processors will be laid out in 2D as
59 // 0 2 4 6
60 // 1 3 5 7
61 //
62 // The matrix will be distributed using np=8 row blocks:
63 // 0 2 4 6
64 // 1 3 5 7
65 // 0 2 4 6
66 // 1 3 5 7
67 // 0 2 4 6
68 // 1 3 5 7
69 // 0 2 4 6
70 // 1 3 5 7
71 //
72 // The vector will be distributed linearly or randomly. The row and
73 // column maps will be built to allow only row- or column-based
74 // communication in the matvec.
75 
76 public:
77  using Distribution<gno_t,scalar_t>::me;
78  using Distribution<gno_t,scalar_t>::np;
79  using Distribution<gno_t,scalar_t>::comm;
80  using Distribution<gno_t,scalar_t>::nrows;
81  using Distribution<gno_t,scalar_t>::Mine;
82 
83  Distribution2D(size_t nrows_,
84  const Teuchos::RCP<const Teuchos::Comm<int> > &comm_,
85  const Teuchos::ParameterList &params) :
86  Distribution<gno_t,scalar_t>(nrows_, comm_, params),
87  npRows(-1), npCols(-1)
88  {
89 
90  {
91  const Teuchos::ParameterEntry *pe = params.getEntryPtr("nProcessorRows");
92  if (pe != NULL)
93  npRows = pe->getValue<int>(&npRows);
94  }
95 
96  {
97  const Teuchos::ParameterEntry *pe = params.getEntryPtr("nProcessorCols");
98  if (pe != NULL)
99  npCols = pe->getValue<int>(&npCols);
100  }
101 
102  // Compute the processor configuration npRows * npCols
103 
104  if (npRows == -1 && npCols == -1) { // Compute both npRows and npCols
105  // First guess
106  npRows = (int)(sqrt(np));
107  npCols = np / npRows;
108  // Adjust npRows so that npRows * npCols == np
109  while (npRows * npCols != np) {
110  npRows++;
111  npCols = np / npRows;
112  }
113  }
114  else { // User specified either npRows or npCols
115  if (npRows == -1) // npCols specified; compute npRows
116  npRows = np / npCols;
117  else if (npCols == -1) // npRows specified; compute npCols
118  npCols = np / npRows;
119 
120  if (npCols * npRows != np) {
121  TEUCHOS_TEST_FOR_EXCEPTION(npRows * npCols != np, std::logic_error,
122  "nProcessorCols " << npCols <<
123  " * nProcessorRows " << npRows <<
124  " = " << npCols * npRows <<
125  " must equal nProcessors " << np <<
126  " for 2D distribution");
127  }
128  }
129  if (me == 0)
130  std::cout << "\n2D Distribution: "
131  << "\n npRows = " << npRows
132  << "\n npCols = " << npCols
133  << "\n np = " << np << std::endl;
134 
135  mypCol = this->TWODPCOL(me);
136  mypRow = this->TWODPROW(me);
137  }
138 
139  virtual ~Distribution2D() {};
140 
141  // Return whether this rank owns nonzero (i,j)
142  virtual bool Mine(gno_t i, gno_t j) = 0;
143  inline bool Mine(gno_t i, gno_t j, int p) {return Mine(i,j);}
144 
145 protected:
146 
147  // Return the processor row for rank p
148  inline int TWODPROW(int p) {return (p % npRows);}
149 
150  // Return the processor column for rank p
151  inline int TWODPCOL(int p) {return (p / npRows);}
152 
153  // Return the rank for processor row i and processor column j
154  inline int TWODPRANK(int i, int j) {return (j * npRows + (j+i) % npRows);}
155 
156  int npRows; // Number of processor rows
157  int npCols; // Number of processor columns
158  int mypRow; // This rank's processor row
159  int mypCol; // This rank's processor column
160 };
161 
163 template <typename gno_t, typename scalar_t>
164 class Distribution2DLinear : public Distribution2D<gno_t,scalar_t> {
165 
166 public:
167  using Distribution<gno_t,scalar_t>::me;
168  using Distribution<gno_t,scalar_t>::np;
169  using Distribution<gno_t,scalar_t>::nrows;
170  using Distribution2D<gno_t,scalar_t>::npRows;
171  using Distribution2D<gno_t,scalar_t>::npCols;
172  using Distribution2D<gno_t,scalar_t>::mypRow;
173  using Distribution2D<gno_t,scalar_t>::mypCol;
174 
175  Distribution2DLinear(size_t nrows_,
176  const Teuchos::RCP<const Teuchos::Comm<int> > &comm_,
177  const Teuchos::ParameterList &params) :
178  Distribution2D<gno_t,scalar_t>(nrows_, comm_, params)
179  {
180  // Build vector describing distribution of vector entries across ranks
181  entries.assign(np+1, nrows / np);
182  int nExtraEntries = nrows % np;
183 
184  // Distribute the extra entries evenly among processors.
185  // To evenly distribute them extra entries among processor rows and
186  // columns, we distribute them along diagonals of the matrix distribution.
187  // For our example, assume we have seven extra values (the max possible
188  // with np=8). Then we give one extra entry each to ranks
189  // [0, 3, 4, 7, 1, 2, 5]. For fewer extra entries, we follow the same
190  // order of assignment, and just stop early.
191 
192  for (int cnt = 0, i = 0; (cnt < nExtraEntries) && (i < npRows); i++) {
193  for (int j = 0; (cnt < nExtraEntries) && (j < npCols); cnt++, j++) {
194  int rankForExtra = Distribution2D<gno_t,scalar_t>::TWODPRANK(i, j);
195  entries[rankForExtra+1]++; // Store in rankForExtra+1 to simplify
196  // prefix sum.
197  }
198  }
199 
200  // Perform prefix sum of entries.
201  entries[0] = 0;
202  for (int i = 1; i <= np; i++)
203  entries[i] = entries[i-1] + entries[i];
204  // Now entries contains the first vector entry for each rank.
205 
206  // Column map: Easy; consecutive entries for all ranks in column.
207  int firstRank = mypCol * npRows; // First rank in my column
208  myFirstCol = entries[firstRank];
209 
210  gno_t nMyCols = 0;
211  for (int i = firstRank; i < firstRank + npRows; i++)
212  nMyCols += entries[i+1] - entries[i];
213  myLastCol = myFirstCol + nMyCols - 1;
214  }
215 
216  inline enum DistributionType DistType() { return TwoDLinear; }
217 
218  bool Mine(gno_t i, gno_t j) {
219  int idx = int(float(i) * float(np) / float(nrows));
220  while (i < entries[idx]) idx--;
221  while (i >= entries[idx+1]) idx++;
222  return ((mypRow == Distribution2D<gno_t,scalar_t>::TWODPROW(idx)) // RowMine
223  && (j >= myFirstCol && j <= myLastCol)); // ColMine
224  }
225 
226  inline bool VecMine(gno_t i) {
227  return(i >= entries[me] && i < entries[me+1]);
228  }
229 
230 
231 private:
232  std::vector<gno_t> entries; // Describes vector entries' distribution to ranks
233  // Organized like vtxdist
234  gno_t myFirstCol; // First column owned by this rank
235  gno_t myLastCol; // Last column owned by this rank
236 };
237 
239 template <typename gno_t, typename scalar_t>
240 class Distribution2DRandom : public Distribution2D<gno_t,scalar_t> {
241 
242 public:
243  using Distribution<gno_t,scalar_t>::me;
244  using Distribution2D<gno_t,scalar_t>::mypRow;
245  using Distribution2D<gno_t,scalar_t>::mypCol;
246 
247  Distribution2DRandom(size_t nrows_,
248  const Teuchos::RCP<const Teuchos::Comm<int> > &comm_,
249  const Teuchos::ParameterList &params) :
250  Distribution2D<gno_t,scalar_t>(nrows_, comm_, params)
251  { if (me == 0) std::cout << " randomize = true" << std::endl; }
252 
253  inline enum DistributionType DistType() { return TwoDRandom; }
254 
255  inline bool Mine(gno_t i, gno_t j) {
256  return ((mypRow == this->TWODPROW(this->HashToProc(i))) && // RowMine
257  (mypCol == this->TWODPCOL(this->HashToProc(j)))); // ColMine
258  }
259 
260  inline bool VecMine(gno_t i) { return (me == this->HashToProc(i)); }
261 
262 };
263 
265 
266 template <typename gno_t, typename scalar_t>
267 class Distribution2DVec : public Distribution2D<gno_t,scalar_t>
268 {
269 // Distribute non-zeros in a 2D manner based on the vector distribution
270 // and the nprows x npcols configuration;
271 // rows are assigned to same process owning the vector entry.
272 public:
273  using Distribution<gno_t,scalar_t>::me;
274  using Distribution<gno_t,scalar_t>::np;
275  using Distribution<gno_t,scalar_t>::comm;
276  using Distribution<gno_t,scalar_t>::nrows;
277  using Distribution2D<gno_t,scalar_t>::npRows;
278  using Distribution2D<gno_t,scalar_t>::npCols;
279 
280  Distribution2DVec(size_t nrows_,
281  const Teuchos::RCP<const Teuchos::Comm<int> > &comm_,
282  const Teuchos::ParameterList &params,
283  std::string &distributionfile) :
284  Distribution2D<gno_t,scalar_t>(nrows_, comm_, params)
285  {
286  if (me == 0) std::cout << "\n 2DVec Distribution: "
287  << "\n np = " << np << std::endl;
288  std::ifstream fpin;
289  if (me == 0) {
290  fpin.open(distributionfile.c_str(), std::ios::in);
291  if (!fpin.is_open()) {
292  std::cout << "Error: distributionfile " << distributionfile
293  << " not found" << std::endl;
294  exit(-1);
295  }
296  }
297 
298  // Read the vector part assignment and broadcast it to all processes.
299  // Broadcast in chunks of bcastsize values.
300  // TODO: Make the vector part assignment more scalable instead of
301  // TODO: storing entire vector on every process.
302 
303  vecpart = new int[nrows];
304 
305  const int bcastsize = 1000000;
306 
307  gno_t start = 0;
308  int cnt = 0;
309  for (size_t i = 0; i < nrows; i++) {
310  if (me == 0) fpin >> vecpart[i];
311  cnt++;
312  if (cnt == bcastsize || i == nrows-1) {
313  Teuchos::broadcast(*comm, 0, cnt, &(vecpart[start]));
314  start += cnt;
315  cnt = 0;
316  }
317  }
318 
319  if (me == 0) fpin.close();
320  }
321 
322  ~Distribution2DVec() {delete [] vecpart;}
323 
324  inline enum DistributionType DistType() { return TwoDVec; }
325 
326  bool Mine(gno_t i, gno_t j) {
327  return (me == (vecpart[i] % npRows + (vecpart[j] / npRows) * npRows));
328  }
329 
330  inline bool VecMine(gno_t i) { return(vecpart[i] == me); }
331 
332 private:
333  int *vecpart;
334 
335 };
336 
337 }
338 #endif
Namespace Tpetra contains the class and methods constituting the Tpetra library.