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