COMBINATORIAL_BLAS  1.3
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
DistEdgeList.cpp
Go to the documentation of this file.
1 /****************************************************************/
2 /* Parallel Combinatorial BLAS Library (for Graph Computations) */
3 /* version 1.2 -------------------------------------------------*/
4 /* date: 10/06/2011 --------------------------------------------*/
5 /* authors: Aydin Buluc (abuluc@lbl.gov), Adam Lugowski --------*/
6 /****************************************************************/
7 /*
8 Copyright (c) 2011, Aydin Buluc
9 
10 Permission is hereby granted, free of charge, to any person obtaining a copy
11 of this software and associated documentation files (the "Software"), to deal
12 in the Software without restriction, including without limitation the rights
13 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 copies of the Software, and to permit persons to whom the Software is
15 furnished to do so, subject to the following conditions:
16 
17 The above copyright notice and this permission notice shall be included in
18 all copies or substantial portions of the Software.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 THE SOFTWARE.
27 */
28 
29 #include <mpi.h>
30 
31 #include "SpParMat.h"
32 #include "ParFriends.h"
33 #include "Operations.h"
34 
35 #ifndef GRAPH_GENERATOR_SEQ
36 #define GRAPH_GENERATOR_SEQ
37 #endif
38 
41 #include "RefGen21.h"
42 
43 #include <fstream>
44 #include <algorithm>
45 using namespace std;
46 
47 template <typename IT>
48 DistEdgeList<IT>::DistEdgeList(): edges(NULL), pedges(NULL), nedges(0), globalV(0)
49 {
50  commGrid.reset(new CommGrid(MPI_COMM_WORLD, 0, 0));
51 }
52 
53 template <typename IT>
54 DistEdgeList<IT>::DistEdgeList(const char * filename, IT globaln, IT globalm): edges(NULL), pedges(NULL), globalV(globaln)
55 {
56  commGrid.reset(new CommGrid(MPI_COMM_WORLD, 0, 0));
57 
58  int nprocs = commGrid->GetSize();
59  int rank = commGrid->GetRank();
60  nedges = (rank == nprocs-1)? (globalm - rank * (globalm / nprocs)) : (globalm / nprocs);
61 
62  FILE * infp = fopen(filename, "rb");
63  assert(infp != NULL);
64  IT read_offset_start, read_offset_end;
65  read_offset_start = rank * 8 * (globalm / nprocs);
66  read_offset_end = (rank+1) * 8 * (globalm / nprocs);
67  if (rank == nprocs - 1)
68  read_offset_end = 8*globalm;
69 
70 
71  ofstream oput;
72  commGrid->OpenDebugFile("BinRead", oput);
73  if(infp != NULL)
74  {
75  oput << "File exists" << endl;
76  oput << "Trying to read " << nedges << " edges out of " << globalm << endl;
77  }
78  else
79  {
80  oput << "File does not exist" << endl;
81  }
82 
83  /* gen_edges is an array of unsigned ints of size 2*nedges */
84  uint32_t * gen_edges = new uint32_t[2*nedges];
85  fseek(infp, read_offset_start, SEEK_SET);
86  fread(gen_edges, 2*nedges, sizeof(uint32_t), infp);
87  SetMemSize(nedges);
88  oput << "Freads done " << endl;
89  for(IT i=0; i< 2*nedges; ++i)
90  edges[i] = (IT) gen_edges[i];
91  oput << "Puts done " << endl;
92  delete [] gen_edges;
93  oput.close();
94 
95 }
96 
97 template <typename IT>
98 void DistEdgeList<IT>::Dump64bit(string filename)
99 {
100  int rank,nprocs;
101  MPI_Comm World = commGrid->GetWorld();
102  MPI_Comm_rank(World, &rank);
103  MPI_Comm_size(World, &nprocs);
104  MPI_File thefile;
105  MPI_File_open(World, filename.c_str(), MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile);
106 
107  IT * prelens = new IT[nprocs];
108  prelens[rank] = 2*nedges;
109  MPI_Allgather(MPI_IN_PLACE, 0, MPIType<IT>(), prelens, 1, MPIType<IT>(), commGrid->GetWorld());
110  IT lengthuntil = accumulate(prelens, prelens+rank, 0);
111 
112  // The disp displacement argument specifies the position
113  // (absolute offset in bytes from the beginning of the file)
114  MPI_File_set_view(thefile, int64_t(lengthuntil * sizeof(IT)), MPIType<IT>(), MPIType<IT>(), "native", MPI_INFO_NULL);
115  MPI_File_write(thefile, edges, prelens[rank], MPIType<IT>(), NULL);
116  MPI_File_close(&thefile);
117  delete [] prelens;
118 }
119 
120 template <typename IT>
121 void DistEdgeList<IT>::Dump32bit(string filename)
122 {
123  int rank, nprocs;
124  MPI_Comm World = commGrid->GetWorld();
125  MPI_Comm_rank(World, &rank);
126  MPI_Comm_size(World, &nprocs);
127  MPI_File thefile;
128  MPI_File_open(World, filename.c_str(), MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile);
129 
130  IT * prelens = new IT[nprocs];
131  prelens[rank] = 2*nedges;
132  MPI_Allgather(MPI_IN_PLACE, 0, MPIType<IT>(), prelens, 1, MPIType<IT>(), commGrid->GetWorld());
133  IT lengthuntil = accumulate(prelens, prelens+rank, static_cast<IT>(0));
134 
135  // The disp displacement argument specifies the position
136  // (absolute offset in bytes from the beginning of the file)
137  MPI_File_set_view(thefile, int64_t(lengthuntil * sizeof(uint32_t)), MPI_UNSIGNED, MPI_UNSIGNED, "native", MPI_INFO_NULL);
138  uint32_t * gen_edges = new uint32_t[prelens[rank]];
139  for(IT i=0; i< prelens[rank]; ++i)
140  gen_edges[i] = (uint32_t) edges[i];
141 
142  MPI_File_write(thefile, gen_edges, prelens[rank], MPI_UNSIGNED, NULL);
143  MPI_File_close(&thefile);
144 
145  delete [] prelens;
146  delete [] gen_edges;
147 }
148 
149 template <typename IT>
151 {
152  if(edges) delete [] edges;
153  if(pedges) delete [] pedges;
154 }
155 
157 template <typename IT>
159 {
160  if (edges)
161  {
162  delete [] edges;
163  edges = NULL;
164  }
165 
166  memedges = ne;
167  edges = 0;
168 
169  if (memedges > 0)
170  edges = new IT[2*memedges];
171 }
172 
177 template <typename IT>
179 {
180 
181  // find out how many edges there actually are
182  while (nedges > 0 && edges[2*(nedges-1) + 0] == -1)
183  {
184  nedges--;
185  }
186 
187  // remove marked multiplicities or self-loops
188  for (IT i = 0; i < (nedges-1); i++)
189  {
190  if (edges[2*i + 0] == -1)
191  {
192  // the graph500 generator marked this edge as a self-loop or a multiple edge.
193  // swap it with the last edge
194  edges[2*i + 0] = edges[2*(nedges-1) + 0];
195  edges[2*i + 1] = edges[2*(nedges-1) + 1];
196  edges[2*(nedges-1) + 0] = -1; // mark this spot as unused
197 
198  while (nedges > 0 && edges[2*(nedges-1) + 0] == -1) // the swapped edge might be -1 too
199  nedges--;
200  }
201  }
202 }
203 
204 
210 template <typename IT>
211 void DistEdgeList<IT>::GenGraph500Data(double initiator[4], int log_numverts, int edgefactor, bool scramble, bool packed)
212 {
213  if(packed && (!scramble))
214  {
215  SpParHelper::Print("WARNING: Packed version does always generate scrambled vertex identifiers\n");
216  }
217 
218  globalV = ((int64_t)1)<< log_numverts;
219  int64_t globaledges = globalV * static_cast<int64_t>(edgefactor);
220 
221  if(packed)
222  {
223  RefGen21::make_graph (log_numverts, globaledges, &nedges, (packed_edge**)(&pedges));
224  }
225  else
226  {
227  // The generations use different seeds on different processors, generating independent
228  // local RMAT matrices all having vertex ranges [0,...,globalmax-1]
229  // Spread the two 64-bit numbers into five nonzero values in the correct range
230  uint_fast32_t seed[5];
231 
232  uint64_t size = (uint64_t) commGrid->GetSize();
233  uint64_t rank = (uint64_t) commGrid->GetRank();
234  #ifdef DETERMINISTIC
235  uint64_t seed2 = 2;
236  #else
237  uint64_t seed2 = time(NULL);
238  #endif
239  make_mrg_seed(rank, seed2, seed); // we give rank as the first seed, so it is different on processors
240 
241  // a single pair of [val0,val1] for all the computation, global across all processors
242  uint64_t val0, val1; /* Values for scrambling */
243  if(scramble)
244  {
245  if(rank == 0)
246  RefGen21::MakeScrambleValues(val0, val1, seed); // ignore the return value
247 
248  MPI_Bcast(&val0, 1, MPIType<uint64_t>(),0, commGrid->GetWorld());
249  MPI_Bcast(&val1, 1, MPIType<uint64_t>(),0, commGrid->GetWorld());
250  }
251 
252  nedges = globaledges/size;
253  SetMemSize(nedges);
254  // clear the source vertex by setting it to -1
255  for (IT i = 0; i < nedges; i++)
256  edges[2*i+0] = -1;
257 
258  generate_kronecker(0, 1, seed, log_numverts, nedges, initiator, edges);
259  if(scramble)
260  {
261  for(IT i=0; i < nedges; ++i)
262  {
263  edges[2*i+0] = RefGen21::scramble(edges[2*i+0], log_numverts, val0, val1);
264  edges[2*i+1] = RefGen21::scramble(edges[2*i+1], log_numverts, val0, val1);
265  }
266  }
267  }
268 }
269 
270 
281 template <typename IT>
283 {
284  IT maxedges = DEL.memedges; // this can be optimized by calling the clean-up first
285 
286  // to lower memory consumption, rename in stages
287  // this is not "identical" to a full randomization;
288  // but more than enough to destroy any possible locality
289  IT stages = 8;
290  IT perstage = maxedges / stages;
291 
292  int nproc =(DEL.commGrid)->GetSize();
293  int rank = (DEL.commGrid)->GetRank();
294  IT * dist = new IT[nproc];
295 
296  MTRand M; // generate random numbers with Mersenne Twister
297  for(IT s=0; s< stages; ++s)
298  {
299  #ifdef DEBUG
300  SpParHelper::Print("PermEdges stage starting\n");
301  double st = MPI_Wtime();
302  #endif
303 
304  IT n_sofar = s*perstage;
305  IT n_thisstage = ((s==(stages-1))? (maxedges - n_sofar): perstage);
306 
307  pair<double, pair<IT,IT> >* vecpair = new pair<double, pair<IT,IT> >[n_thisstage];
308  dist[rank] = n_thisstage;
309  MPI_Allgather(MPI_IN_PLACE, 1, MPIType<IT>(), dist, 1, MPIType<IT>(), DEL.commGrid->GetWorld());
310 
311  for (IT i = 0; i < n_thisstage; i++)
312  {
313  vecpair[i].first = M.rand();
314  vecpair[i].second.first = DEL.edges[2*(i+n_sofar)];
315  vecpair[i].second.second = DEL.edges[2*(i+n_sofar)+1];
316  }
317 
318  // less< pair<T1,T2> > works correctly (sorts w.r.t. first element of type T1)
319  SpParHelper::MemoryEfficientPSort(vecpair, n_thisstage, dist, DEL.commGrid->GetWorld());
320  // SpParHelper::DebugPrintKeys(vecpair, n_thisstage, dist, DEL.commGrid->GetWorld());
321  for (IT i = 0; i < n_thisstage; i++)
322  {
323  DEL.edges[2*(i+n_sofar)] = vecpair[i].second.first;
324  DEL.edges[2*(i+n_sofar)+1] = vecpair[i].second.second;
325  }
326  delete [] vecpair;
327  #ifdef DEBUG
328  double et = MPI_Wtime();
329  ostringstream timeinfo;
330  timeinfo << "Stage " << s << " in " << et-st << " seconds" << endl;
331  SpParHelper::Print(timeinfo.str());
332  #endif
333  }
334  delete [] dist;
335 }
336 
347 template <typename IU>
349 {
350  int nprocs = DEL.commGrid->GetSize();
351  int rank = DEL.commGrid->GetRank();
352  MPI_Comm World = DEL.commGrid->GetWorld();
353 
354  // create permutation
355  FullyDistVec<IU, IU> globalPerm(DEL.commGrid);
356  globalPerm.iota(DEL.getGlobalV(), 0);
357  globalPerm.RandPerm(); // now, randperm can return a 0-based permutation
358  IU locrows = globalPerm.MyLocLength();
359 
360  // way to mark whether each vertex was already renamed or not
361  IU locedgelist = 2*DEL.getNumLocalEdges();
362  bool* renamed = new bool[locedgelist];
363  fill_n(renamed, locedgelist, 0);
364 
365  // permutation for one round
366  IU * localPerm = NULL;
367  IU permsize;
368  IU startInd = 0;
369 
370  //vector < pair<IU, IU> > vec;
371  //for(IU i=0; i< DEL.getNumLocalEdges(); i++)
372  // vec.push_back(make_pair(DEL.edges[2*i], DEL.edges[2*i+1]));
373  //sort(vec.begin(), vec.end());
374  //vector < pair<IU, IU> > uniqued;
375  //unique_copy(vec.begin(), vec.end(), back_inserter(uniqued));
376  //cout << "before: " << vec.size() << " and after: " << uniqued.size() << endl;
377 
378  for (int round = 0; round < nprocs; round++)
379  {
380  // broadcast the permutation from the one processor
381  if (rank == round)
382  {
383  permsize = locrows;
384  localPerm = new IU[permsize];
385  copy(globalPerm.arr.begin(), globalPerm.arr.end(), localPerm);
386  }
387  MPI_Bcast(&permsize, 1, MPIType<IU>(), round, World);
388  if(rank != round)
389  {
390  localPerm = new IU[permsize];
391  }
392  MPI_Bcast(localPerm, permsize, MPIType<IU>(), round, World);
393 
394  // iterate over
395  for (typename vector<IU>::size_type j = 0; j < (unsigned)locedgelist ; j++)
396  {
397  // We are renaming vertices, not edges
398  if (startInd <= DEL.edges[j] && DEL.edges[j] < (startInd + permsize) && !renamed[j])
399  {
400  DEL.edges[j] = localPerm[DEL.edges[j]-startInd];
401  renamed[j] = true;
402  }
403  }
404  startInd += permsize;
405  delete [] localPerm;
406  }
407  delete [] renamed;
408 }
409