Flow123d  release_2.2.0-41-g0958a8d
sparse_graph.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file sparse_graph.cc
15  * @ingroup la
16  * @brief Construction and partitioning of a sparse graph
17  */
18 
19 #include "system/global_defs.h"
20 #include "system/system.hh"
21 #include "la/distribution.hh"
22 #include "sparse_graph.hh"
23 
24 #include "boost/lambda/lambda.hpp"
25 #include <algorithm>
26 
27 extern "C" {
28 #include <metis.h>
29 }
30 
31 
32 /*****************************************************************************************
33  SPARSE GRAPH
34  *****************************************************************************************/
35 
36 
38  : vtx_distr(distr),
39  rows(NULL),
40  adj(NULL),
41  adj_weights(NULL),
42  part_to_check(NULL),
43  adj_of_proc( vtx_distr.np() )
44 {
45  // positions only of local vertexes
46  vtx_XYZ= new float[vtx_distr.lsize()+1];
47  vtx_weights= new int[vtx_distr.lsize()+1];
48  for(unsigned int i=0; i<vtx_distr.lsize(); i++) vtx_weights[i]=1;
49 }
50 
51 
52 
54  : vtx_distr(loc_size, comm),
55  rows(NULL),
56  adj(NULL),
57  adj_weights(NULL),
58  adj_of_proc( vtx_distr.np() )
59 {
60  // positions only of local vertexes
61  vtx_XYZ= new float[vtx_distr.lsize()+1];
62  vtx_weights= new int[vtx_distr.lsize()+1];
63  for(unsigned int i=0; i<vtx_distr.lsize(); i++) vtx_weights[i]=1;
64 }
65 
66 void SparseGraph::set_edge(const int a, const int b,int weight)
67 {
68  Edge e={a,b, weight};
69 
70  adj_of_proc[vtx_distr.get_proc(a)].push(e);
71 }
72 
73 
74 
75 
76 void SparseGraph::set_vtx_position(const int vtx, const float xyz[3],int weight)
77 {
78  OLD_ASSERT(vtx_distr.is_local(vtx),"Can not set vertex position for nonlocal vertex %d.\n",vtx);
79  int loc_index=vtx-vtx_distr.begin();
80  memcpy(vtx_XYZ+3*loc_index, xyz, 3*sizeof(float));
81  vtx_weights[loc_index]=weight;
82 }
83 
84 
85 /**
86  * Edge comparison. For lexical sorting of local edges.
87  */
88 
90 {
91  return (a.from < b.from ||
92  ( a.from == b.from && a.to < b.to));
93 }
94 
95 
96 // TODO: split into smaller functions
97 // check trivial case : Localized distribution
99 {
100  OLD_ASSERT( adj==NULL, "Graph is already finalized\n");
101 
102  unsigned int proc;
103  int total_size;
104  vector< stack<Edge> >::iterator s;
105  unsigned int edge_size=3; // 3 = number of integers in Edge to send
106 
107  /////////////////////////////////////
108  // communicate inserted edges
109 
110  // unroll data in adj_of_proc into send buffer
111  int * sdispls = new int [vtx_distr.np()];
112  int * scounts = new int [vtx_distr.np()];
113 
114  total_size=0;
115  for(proc=0, s=adj_of_proc.begin(); s!=adj_of_proc.end();++s, ++proc)
116  {
117  sdispls[proc] = total_size;
118  scounts[proc] = edge_size * (s)->size();
119  total_size += edge_size * ((s)->size)();
120  }
121  int * sendbuf = new int [(total_size+1)];
122 
123  Edge edge;
124  int buf_pos=0;
125  for(proc=0, s = adj_of_proc.begin(); s!=adj_of_proc.end(); ++s, ++proc)
126  {
127  OLD_ASSERT(sdispls[proc] == buf_pos,
128  "Mismatch between displacement %d and buffer position %d. \n", sdispls[proc], buf_pos );
129  while ( ! (s)->empty() ) {
130  edge=(s)->top();
131  sendbuf[buf_pos++]=edge.from;
132  sendbuf[buf_pos++]=edge.to;
133  sendbuf[buf_pos++]=edge.weight;
134  (s)->pop();
135  }
136  }
137 
138  // communicate send sizes
139  int * rcounts = new int [vtx_distr.np()];
140  int * rdispls = new int [vtx_distr.np()];
141  MPI_Alltoall( scounts, 1, MPI_INT, rcounts, 1, MPI_INT, vtx_distr.get_comm());
142 
143  // prepare receiving buffers
144  total_size=0;
145  for(proc=0; proc<vtx_distr.np(); proc++)
146  {
147  rdispls[proc] = total_size;
148  total_size += rcounts[proc];
149  }
150 
151  int * recvbuf = new int [total_size+1];
152 
153  MPI_Alltoallv (
154  sendbuf, scounts, sdispls, MPI_INT,
155  recvbuf, rcounts, rdispls, MPI_INT,
156  vtx_distr.get_comm() );
157 
158  delete [] sendbuf;
159  delete [] scounts;
160  delete [] sdispls;
161  delete [] rcounts;
162  delete [] rdispls;
163 
164  /////////////////////////////////////
165  // construct local adj and rows arrays
166  Edge *edges= (Edge *) recvbuf;
167  int size=total_size/edge_size;
168  std::sort(edges, edges + size);
169 
170  allocate_sparse_graph(vtx_distr.lsize() + 1, size+1);
171  rows[0]=0;
172 
173  if (size != 0) {
174 
175  int i;
176  int row=0;
177  int i_adj=0;
178  int loc_from;
179  Edge unknown_edge={-1,-1,0};
180  Edge *last_edge=&unknown_edge;
181 
182  for(i=0;i<size;i++) {
183  if (! (*last_edge < edges[i]) ) continue; // skip equivalent edges
184  last_edge=edges+i;
185 
186  OLD_ASSERT(vtx_distr.is_local(edges[i].from),
187  "Received non-local edge: %d %d at position %d\n",edges[i].from, edges[i].to,i);
188 
189  loc_from=edges[i].from-vtx_distr.begin();
190  OLD_ASSERT( row <= loc_from, "Decrease in sorted edges at %d\n",i);
191 
192  while ( row < loc_from ) rows[++row]=i;
193  adj[i_adj]=edges[i].to;
194  adj_weights[i_adj]=edges[i].weight;
195  i_adj++;
196  }
197  rows[++row]=i; // i==size (of adj array)
198 
199  }
200 
201  delete [] recvbuf;
202 }
203 
204 
205 /**
206  * Check if the subgraphs of the partitions are connected.
207  */
209 {
210  OLD_ASSERT( vtx_distr.lsize(0)==vtx_distr.size() , "Check of graph continuity not yet implemented for paralel case.\n");
211  if (vtx_distr.myp()!=0) return(true);
212 
213  part_to_check=part;
214  checked_vtx.resize(vtx_distr.size(), 0);
215  std::vector<bool> checked_proc(vtx_distr.np(), false);
216 
217  unsigned int n_proc=0;
218  for(unsigned int vtx=0; n_proc<vtx_distr.np() && vtx<vtx_distr.size(); vtx++) {
219  if (checked_vtx[vtx] != 2) {
221  // check if the processor is still unvisited
222  if ( checked_proc[proc_to_check] )
223  WarningOut().fmt("Disconnected subgraph {} detected at vertex {}.\n", part_to_check[vtx], vtx);
224  // DFS unvisited vertex
225  checked_vtx[vtx]=1;
226  DFS(vtx);
227  checked_proc[proc_to_check]=true;
228  n_proc++;
229  }
230  }
231  checked_vtx.clear();
232 
233  DebugOut() << "Connectivity of subgraphs is OK.\n";
234  return (true);
235 }
236 
237 /**
238  * Recursive part of Deep First Search algorithm of the connectivity check.
239  */
240 void SparseGraph::DFS(int vtx)
241 {
242  OLD_ASSERT( vtx>=0 && vtx< (int) vtx_distr.size(),"Invalid entry vertex %d in DFS.\n",vtx);
243  int neighbour;
244  for(int i_neigh=rows[vtx]; i_neigh< rows[vtx+1];i_neigh++) {
245  neighbour = adj[i_neigh];
246  if (part_to_check[neighbour] == proc_to_check && checked_vtx[neighbour] == 0) {
247  checked_vtx[neighbour]=1;
248  DFS(neighbour);
249  }
250  }
251  checked_vtx[vtx]=2;
252 }
253 
254 
255 
257 {
258  OLD_ASSERT( adj,"Can not view non finalized graph.\n");
259  int row,col;
260  MessageOut() << "SparseGraph\n";
261  for(row=0; row < (int) vtx_distr.lsize(); row++) {
262  MessageOut().fmt("edges from this vertex: {}\n", rows[row+1]);
263  for(col=rows[row]; col<rows[row+1]; col++) {
264  MessageOut().fmt("edge (v1, v2): {} {}\n", row+vtx_distr.begin(), adj[col]);
265  }
266  }
267 }
268 
269 
271 {
272  OLD_ASSERT( rows && adj, "Graph is not yet finalized.");
273 
274  int loc_row, row, row_pos;
275  int col_pos,col,loc_col;
276 
277  for(loc_row=0;loc_row< (int) vtx_distr.lsize();loc_row++) {
278  row=loc_row+vtx_distr.begin();
279  for(row_pos=rows[loc_row];row_pos<rows[loc_row+1];row_pos++) {
280  col=adj[row_pos];
281  if (vtx_distr.is_local(col)) {
282  // find the local column
283  loc_col=col-vtx_distr.begin();
284  for(col_pos=rows[loc_col]; col_pos<rows[loc_col+1]; col_pos++)
285  if (adj[col_pos]==row) break;
286  if (adj[col_pos]!=row) return false;
287  }
288  }
289  }
290 
291  return true;
292 }
293 
294 
295 /**
296  * Free all non-null pointers.
297  */
299 {
300  delete[] vtx_XYZ;
301  delete[] vtx_weights;
302 }
303 
304 /*!
305  * @brief Output a sparse graph.
306  * @return Changed ostream.
307  */
308 ostream & operator <<(ostream & out, const SparseGraph &fcall)
309 {
310  // TODO
311  return out; //dodelat...
312 }
313 
314 
315 /*****************************************************************************************
316  SPARSE GRAPH PETSC
317  *****************************************************************************************/
318 
319 void SparseGraphPETSC::allocate_sparse_graph(int lsize_vtxs, int lsize_adj)
320 {
321  PetscMalloc(lsize_vtxs*sizeof(int), &rows);
322  PetscMalloc(lsize_adj*sizeof(int),&adj);
323  PetscMalloc(lsize_adj*sizeof(int),&adj_weights);
324 }
325 
326 
327 void SparseGraphPETSC::partition(int *loc_part)
328 {
329  OLD_ASSERT( adj && rows,"Can not make partition of non finalized graph.\n");
330 
331  MatCreateMPIAdj(vtx_distr.get_comm(), vtx_distr.lsize(),vtx_distr.size(),
332  rows, adj,adj_weights, &petsc_adj_mat);
333  MatPartitioningCreate(vtx_distr.get_comm(),& petsc_part);
334  MatPartitioningSetAdjacency(petsc_part, petsc_adj_mat);
335  MatPartitioningSetFromOptions(petsc_part);
336  MatPartitioningApply(petsc_part,&part_IS);
337 
338  // copy the partition array
339  const PetscInt *is_array;
340  ISGetIndices(part_IS, &is_array);
341  memcpy(loc_part, is_array, vtx_distr.lsize()*sizeof(int));
342  ISRestoreIndices(part_IS, &is_array);
343 }
344 
345 
347 {
348  ISDestroy(&part_IS);
349  MatPartitioningDestroy(&petsc_part);
350  MatDestroy(&petsc_adj_mat);
351 
352  if (adj) PetscFree(adj);
353  if (rows) PetscFree(rows);
354  if (adj_weights) PetscFree(adj_weights);
355 
356  // automaticaly calls base class destructor
357 }
358 
359 
360 /*****************************************************************************************
361  SPARSE GRAPH METIS
362  *****************************************************************************************/
363 
364 
365 void SparseGraphMETIS::allocate_sparse_graph(int lsize_vtxs, int lsize_adj)
366 {
367  rows = new int[lsize_vtxs];
368  adj = new int[lsize_adj];
369  adj_weights = new int [lsize_adj];
370 }
371 
372 
374 {
376  "METIS could be used only with localized distribution.\n");
377  if (vtx_distr.np()==1) {
378  for(unsigned int i=0;i<vtx_distr.size();i++) part[i]=0;
379  return;
380  } else {
381  if (vtx_distr.myp()==0) {
382  int n_vtx=vtx_distr.size();
383  int n_proc=vtx_distr.np();
384  int num_flag=0; // indexing style (0-C, 1-Fortran)
385  int edgecut;
386 
387 /***********************************************************************************
388  * SETTING OPTIONS
389  */
390 #if (METIS_VER_MAJOR >= 5)
391  if ( sizeof(idx_t) != sizeof(int) ) {
392  printf("ERROR in GRAPH_DIVIDE_C: Wrong type of integers for METIS.\n");
393  abort();
394  }
395  int ncon = 1;
396  // Unbalance tolerance vector. Multi-criteria balance is available in METIS 5.x.
397  real_t ubvec[1];
398  ubvec[0] = 1.001; // This is in fact default value used by METIS.
399  int options[METIS_NOPTIONS];
400 
401  for (unsigned int i = 0;i < METIS_NOPTIONS;i++) options[i] = -1;
402 
403  options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_CUT;
404  options[METIS_OPTION_CTYPE] = METIS_CTYPE_RM;
405  options[METIS_OPTION_IPTYPE] = METIS_IPTYPE_GROW;
406  options[METIS_OPTION_RTYPE] = METIS_RTYPE_GREEDY;
407  options[METIS_OPTION_NCUTS] = 1;
408  options[METIS_OPTION_NSEPS] = 1;
409  options[METIS_OPTION_NUMBERING] = num_flag;
410  options[METIS_OPTION_NITER] = 10;
411  options[METIS_OPTION_SEED] = 12345;
412  options[METIS_OPTION_MINCONN] = 1;
413  options[METIS_OPTION_CONTIG] = 0;
414  options[METIS_OPTION_COMPRESS] = 0;
415  options[METIS_OPTION_CCORDER] = 0;
416  options[METIS_OPTION_UFACTOR] = 30;
417  //options[METIS_OPTION_DBGLVL] = METIS_DBG_INFO | METIS_DBG_TIME;
418  options[METIS_OPTION_DBGLVL] = 0;
419 #else
420  /* weights */
421  int wgtflag=3;
422  int options[5];
423  for (unsigned int i = 0; i < 5; i++ ) options[i] = 0;
424 
425 #endif
426 
427 
428  /* Initialize parts */
429  for (unsigned int i = 0; i < vtx_distr.lsize(); i++ ) part[i] = num_flag;
430 
431 /***********************************************************************************
432  * CALL METIS using optimal algorithm depending on the number of partitions
433  */
434 
435  if (n_proc <= 8) {
436 
437 #if (METIS_VER_MAJOR >= 5)
438  options[METIS_OPTION_PTYPE] = METIS_PTYPE_RB;
439  options[METIS_OPTION_UFACTOR] = 1;
440  METIS_PartGraphRecursive(&n_vtx, &ncon, rows, adj,
441  vtx_weights, NULL, adj_weights, &n_proc, NULL,
442  ubvec, options, &edgecut,part);
443 #else
444  METIS_PartGraphRecursive(&n_vtx, rows, adj,
445  vtx_weights, adj_weights, &wgtflag, &num_flag,
446  &n_proc, options, &edgecut, part);
447 #endif
448  } else {
449 
450 #if (METIS_VER_MAJOR >= 5)
451  options[METIS_OPTION_PTYPE] = METIS_PTYPE_KWAY;
452  options[METIS_OPTION_UFACTOR] = 30;
453  METIS_PartGraphKway(&n_vtx, &ncon, rows, adj,
454  vtx_weights, NULL, adj_weights, &n_proc, NULL,
455  ubvec, options, &edgecut, part);
456 #else
457  METIS_PartGraphKway(&n_vtx,rows,adj, //vtx distr, local vtx begins, edges of local vtxs
458  vtx_weights,adj_weights,&wgtflag,&num_flag, // vertex, edge weights, ...
459  &n_proc,options,&edgecut,part);
460 #endif
461  }
462  }
463  }
464 }
465 
467 {
468  if (adj) delete[](adj);
469  if (rows) delete[](rows);
470  if (adj_weights) delete[](adj_weights);
471 
472  // automaticaly calls base class destructor
473 }
unsigned int size() const
get global size
unsigned int get_proc(unsigned int idx) const
get processor of the given index
ostream & operator<<(ostream &out, const SparseGraph &fcall)
Output a sparse graph.
int MPI_Comm
Definition: mpi.h:141
bool check_subgraph_connectivity(int *part)
#define MessageOut()
Macro defining &#39;message&#39; record of log.
Definition: logger.hh:233
SparseGraph(const Distribution &distr)
Definition: sparse_graph.cc:37
void DFS(int vtx)
void set_vtx_position(const int vtx, const float xyz[3], int weight=1)
Definition: sparse_graph.cc:76
#define OLD_ASSERT(...)
Definition: global_defs.h:131
virtual ~SparseGraphPETSC()
int * part_to_check
created partitioning used through check of connectivity
Global macros to enhance readability and debugging, general constants.
int weight
Edge weights for communication (optional).
bool is_local(unsigned int idx) const
identify local index
#define MPI_Alltoall(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm)
Definition: mpi.h:595
void finalize()
Make sparse graph structures: rows, adj.
Definition: sparse_graph.cc:98
Virtual class for construction and partitioning of a distributed sparse graph.
Definition: sparse_graph.hh:54
Distribution vtx_distr
distribution of vertexes
virtual void allocate_sparse_graph(int lsize_vtxs, int lsize_adj)=0
unsigned int begin(int proc) const
get starting local index
vector< stack< Edge > > adj_of_proc
storage for graph edges for individual processors
virtual void partition(int *loc_part)
virtual ~SparseGraph()
int proc_to_check
subgraph to check
unsigned int np() const
get num of processors
std::vector< int > checked_vtx
coloring of DFS algorithm
unsigned int myp() const
get my processor
Support classes for parallel programing.
float * vtx_XYZ
optional vertex coordinates (global array)
virtual ~SparseGraphMETIS()
virtual void allocate_sparse_graph(int lsize_vtxs, int lsize_adj)
#define MPI_INT
Definition: mpi.h:160
MPI_Comm get_comm() const
Returns communicator.
int * adj_weights
Distributed sparse graphs, partitioning.
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:236
virtual void allocate_sparse_graph(int lsize_vtxs, int lsize_adj)
int * vtx_weights
Vertex weights for computations (optional).
#define DebugOut()
Macro defining &#39;debug&#39; record of log.
Definition: logger.hh:242
int * adj
sparse adjency
friend bool operator<(const Edge &a, const Edge &b)
Definition: sparse_graph.cc:89
bool is_symmetric()
void set_edge(const int a, const int b, int weight=1)
Definition: sparse_graph.cc:66
virtual void partition(int *loc_part)
#define MPI_Alltoallv(sendbuf, sendcounts, sdispls, sendtype, recvbuf, recvcounts, rdispls, recvtype, comm)
Definition: mpi.h:602
void printf(BasicWriter< Char > &w, BasicCStringRef< Char > format, ArgList args)
Definition: printf.h:444
unsigned int lsize(int proc) const
get local size