1 /*!
2  * \file
3  *
4  * \brief Various routines with dealing with sparse graphs
5  *
6  * \author George Karypis
7  * \version\verbatim $Id: graph.c 13328 2012-12-31 14:57:40Z karypis $ \endverbatim
8  */
9 
10 #include <GKlib.h>
11 
12 #define OMPMINOPS       50000
13 
14 /*************************************************************************/
15 /*! Allocate memory for a graph and initializes it
16     \returns the allocated graph. The various fields are set to NULL.
17 */
18 /**************************************************************************/
gk_graph_Create()19 gk_graph_t *gk_graph_Create()
20 {
21   gk_graph_t *graph;
22 
23   graph = (gk_graph_t *)gk_malloc(sizeof(gk_graph_t), "gk_graph_Create: graph");
24 
25   gk_graph_Init(graph);
26 
27   return graph;
28 }
29 
30 
31 /*************************************************************************/
32 /*! Initializes the graph.
33     \param graph is the graph to be initialized.
34 */
35 /*************************************************************************/
gk_graph_Init(gk_graph_t * graph)36 void gk_graph_Init(gk_graph_t *graph)
37 {
38   memset(graph, 0, sizeof(gk_graph_t));
39   graph->nvtxs = -1;
40 }
41 
42 
43 /*************************************************************************/
44 /*! Frees all the memory allocated for a graph.
45     \param graph is the graph to be freed.
46 */
47 /*************************************************************************/
gk_graph_Free(gk_graph_t ** graph)48 void gk_graph_Free(gk_graph_t **graph)
49 {
50   if (*graph == NULL)
51     return;
52   gk_graph_FreeContents(*graph);
53   gk_free((void **)graph, LTERM);
54 }
55 
56 
57 /*************************************************************************/
58 /*! Frees only the memory allocated for the graph's different fields and
59     sets them to NULL.
60     \param graph is the graph whose contents will be freed.
61 */
62 /*************************************************************************/
gk_graph_FreeContents(gk_graph_t * graph)63 void gk_graph_FreeContents(gk_graph_t *graph)
64 {
65   gk_free((void *)&graph->xadj, &graph->adjncy,
66           &graph->iadjwgt, &graph->fadjwgt,
67           &graph->ivwgts, &graph->fvwgts,
68           &graph->ivsizes, &graph->fvsizes,
69           &graph->vlabels,
70           LTERM);
71 }
72 
73 
74 /**************************************************************************/
75 /*! Reads a sparse graph from the supplied file
76     \param filename is the file that stores the data.
77     \param format is the graph format. The supported values are:
78            GK_GRAPH_FMT_METIS.
79     \param isfewgts is 1 if the edge-weights should be read as floats
80     \param isfvwgts is 1 if the vertex-weights should be read as floats
81     \param isfvsizes is 1 if the vertex-sizes should be read as floats
82     \returns the graph that was read.
83 */
84 /**************************************************************************/
gk_graph_Read(char * filename,int format,int isfewgts,int isfvwgts,int isfvsizes)85 gk_graph_t *gk_graph_Read(char *filename, int format, int isfewgts,
86                 int isfvwgts, int isfvsizes)
87 {
88   ssize_t i, k, l;
89   size_t nfields, nvtxs, nedges, fmt, ncon, lnlen;
90   int32_t ival;
91   float fval;
92   int readsizes=0, readwgts=0, readvals=0, numbering=0;
93   char *line=NULL, *head, *tail, fmtstr[256];
94   FILE *fpin=NULL;
95   gk_graph_t *graph=NULL;
96 
97 
98   if (!gk_fexists(filename))
99     gk_errexit(SIGERR, "File %s does not exist!\n", filename);
100 
101   if (format == GK_GRAPH_FMT_METIS) {
102     fpin = gk_fopen(filename, "r", "gk_graph_Read: fpin");
103     do {
104       if (gk_getline(&line, &lnlen, fpin) <= 0)
105         gk_errexit(SIGERR, "Premature end of input file: file:%s\n", filename);
106     } while (line[0] == '%');
107 
108     fmt = ncon = 0;
109     nfields = sscanf(line, "%zu %zu %zu %zu", &nvtxs, &nedges, &fmt, &ncon);
110     if (nfields < 2)
111       gk_errexit(SIGERR, "Header line must contain at least 2 integers (#vtxs and #edges).\n");
112 
113     nedges *= 2;
114 
115     if (fmt > 111)
116       gk_errexit(SIGERR, "Cannot read this type of file format [fmt=%zu]!\n", fmt);
117 
118     sprintf(fmtstr, "%03zu", fmt%1000);
119     readsizes = (fmtstr[0] == '1');
120     readwgts  = (fmtstr[1] == '1');
121     readvals  = (fmtstr[2] == '1');
122     numbering = 1;
123     ncon      = (ncon == 0 ? 1 : ncon);
124   }
125   else {
126     gk_errexit(SIGERR, "Unrecognized format: %d\n", format);
127   }
128 
129   graph = gk_graph_Create();
130 
131   graph->nvtxs = nvtxs;
132 
133   graph->xadj   = gk_zmalloc(nvtxs+1, "gk_graph_Read: xadj");
134   graph->adjncy = gk_i32malloc(nedges, "gk_graph_Read: adjncy");
135   if (readvals) {
136     if (isfewgts)
137       graph->fadjwgt = gk_fmalloc(nedges, "gk_graph_Read: fadjwgt");
138     else
139       graph->iadjwgt = gk_i32malloc(nedges, "gk_graph_Read: iadjwgt");
140   }
141 
142   if (readsizes) {
143     if (isfvsizes)
144       graph->fvsizes = gk_fmalloc(nvtxs, "gk_graph_Read: fvsizes");
145     else
146       graph->ivsizes = gk_i32malloc(nvtxs, "gk_graph_Read: ivsizes");
147   }
148 
149   if (readwgts) {
150     if (isfvwgts)
151       graph->fvwgts = gk_fmalloc(nvtxs*ncon, "gk_graph_Read: fvwgts");
152     else
153       graph->ivwgts = gk_i32malloc(nvtxs*ncon, "gk_graph_Read: ivwgts");
154   }
155 
156 
157   /*----------------------------------------------------------------------
158    * Read the sparse graph file
159    *---------------------------------------------------------------------*/
160   numbering = (numbering ? - 1 : 0);
161   for (graph->xadj[0]=0, k=0, i=0; i<nvtxs; i++) {
162     do {
163       if (gk_getline(&line, &lnlen, fpin) == -1)
164         gk_errexit(SIGERR, "Pregraphure end of input file: file while reading row %d\n", i);
165     } while (line[0] == '%');
166 
167     head = line;
168     tail = NULL;
169 
170     /* Read vertex sizes */
171     if (readsizes) {
172       if (isfvsizes) {
173 #ifdef __MSC__
174         graph->fvsizes[i] = (float)strtod(head, &tail);
175 #else
176         graph->fvsizes[i] = strtof(head, &tail);
177 #endif
178         if (tail == head)
179           gk_errexit(SIGERR, "The line for vertex %zd does not have size information\n", i+1);
180         if (graph->fvsizes[i] < 0)
181           gk_errexit(SIGERR, "The size for vertex %zd must be >= 0\n", i+1);
182       }
183       else {
184         graph->ivsizes[i] = strtol(head, &tail, 0);
185         if (tail == head)
186           gk_errexit(SIGERR, "The line for vertex %zd does not have size information\n", i+1);
187         if (graph->ivsizes[i] < 0)
188           gk_errexit(SIGERR, "The size for vertex %zd must be >= 0\n", i+1);
189       }
190       head = tail;
191     }
192 
193     /* Read vertex weights */
194     if (readwgts) {
195       for (l=0; l<ncon; l++) {
196         if (isfvwgts) {
197 #ifdef __MSC__
198           graph->fvwgts[i*ncon+l] = (float)strtod(head, &tail);
199 #else
200           graph->fvwgts[i*ncon+l] = strtof(head, &tail);
201 #endif
202           if (tail == head)
203             gk_errexit(SIGERR, "The line for vertex %zd does not have enough weights "
204                     "for the %d constraints.\n", i+1, ncon);
205           if (graph->fvwgts[i*ncon+l] < 0)
206             gk_errexit(SIGERR, "The weight vertex %zd and constraint %zd must be >= 0\n", i+1, l);
207         }
208         else {
209           graph->ivwgts[i*ncon+l] = strtol(head, &tail, 0);
210           if (tail == head)
211             gk_errexit(SIGERR, "The line for vertex %zd does not have enough weights "
212                     "for the %d constraints.\n", i+1, ncon);
213           if (graph->ivwgts[i*ncon+l] < 0)
214             gk_errexit(SIGERR, "The weight vertex %zd and constraint %zd must be >= 0\n", i+1, l);
215         }
216         head = tail;
217       }
218     }
219 
220 
221     /* Read the rest of the row */
222     while (1) {
223       ival = (int)strtol(head, &tail, 0);
224       if (tail == head)
225         break;
226       head = tail;
227 
228       if ((graph->adjncy[k] = ival + numbering) < 0)
229         gk_errexit(SIGERR, "Error: Invalid column number %d at row %zd.\n", ival, i);
230 
231       if (readvals) {
232         if (isfewgts) {
233 #ifdef __MSC__
234           fval = (float)strtod(head, &tail);
235 #else
236     	  fval = strtof(head, &tail);
237 #endif
238           if (tail == head)
239             gk_errexit(SIGERR, "Value could not be found for edge! Vertex:%zd, NNZ:%zd\n", i, k);
240 
241           graph->fadjwgt[k] = fval;
242         }
243         else {
244     	  ival = strtol(head, &tail, 0);
245           if (tail == head)
246             gk_errexit(SIGERR, "Value could not be found for edge! Vertex:%zd, NNZ:%zd\n", i, k);
247 
248           graph->iadjwgt[k] = ival;
249         }
250         head = tail;
251       }
252       k++;
253     }
254     graph->xadj[i+1] = k;
255   }
256 
257   if (k != nedges)
258     gk_errexit(SIGERR, "gk_graph_Read: Something wrong with the number of edges in "
259                        "the input file. nedges=%zd, Actualnedges=%zd.\n", nedges, k);
260 
261   gk_fclose(fpin);
262 
263   gk_free((void **)&line, LTERM);
264 
265   return graph;
266 }
267 
268 
269 /**************************************************************************/
270 /*! Writes a graph into a file.
271     \param graph is the graph to be written,
272     \param filename is the name of the output file.
273     \param format is one of GK_GRAPH_FMT_METIS specifying
274            the format of the output file.
275 */
276 /**************************************************************************/
gk_graph_Write(gk_graph_t * graph,char * filename,int format)277 void gk_graph_Write(gk_graph_t *graph, char *filename, int format)
278 {
279   ssize_t i, j;
280   int hasvwgts, hasvsizes, hasewgts;
281   FILE *fpout;
282 
283   if (format != GK_GRAPH_FMT_METIS)
284     gk_errexit(SIGERR, "Unknown file format. %d\n", format);
285 
286   if (filename)
287     fpout = gk_fopen(filename, "w", "gk_graph_Write: fpout");
288   else
289     fpout = stdout;
290 
291 
292   hasewgts  = (graph->iadjwgt || graph->fadjwgt);
293   hasvwgts  = (graph->ivwgts || graph->fvwgts);
294   hasvsizes = (graph->ivsizes || graph->fvsizes);
295 
296   /* write the header line */
297   fprintf(fpout, "%d %zd", graph->nvtxs, graph->xadj[graph->nvtxs]/2);
298   if (hasvwgts || hasvsizes || hasewgts)
299     fprintf(fpout, " %d%d%d", hasvsizes, hasvwgts, hasewgts);
300   fprintf(fpout, "\n");
301 
302 
303   for (i=0; i<graph->nvtxs; i++) {
304     if (hasvsizes) {
305       if (graph->ivsizes)
306         fprintf(fpout, " %d", graph->ivsizes[i]);
307       else
308         fprintf(fpout, " %f", graph->fvsizes[i]);
309     }
310 
311     if (hasvwgts) {
312       if (graph->ivwgts)
313         fprintf(fpout, " %d", graph->ivwgts[i]);
314       else
315         fprintf(fpout, " %f", graph->fvwgts[i]);
316     }
317 
318     for (j=graph->xadj[i]; j<graph->xadj[i+1]; j++) {
319       fprintf(fpout, " %d", graph->adjncy[j]+1);
320       if (hasewgts) {
321         if (graph->iadjwgt)
322           fprintf(fpout, " %d", graph->iadjwgt[j]);
323         else
324           fprintf(fpout, " %f", graph->fadjwgt[j]);
325       }
326     }
327     fprintf(fpout, "\n");
328   }
329   if (filename)
330     gk_fclose(fpout);
331 }
332 
333 
334 /*************************************************************************/
335 /*! Returns a copy of a graph.
336     \param graph is the graph to be duplicated.
337     \returns the newly created copy of the graph.
338 */
339 /**************************************************************************/
gk_graph_Dup(gk_graph_t * graph)340 gk_graph_t *gk_graph_Dup(gk_graph_t *graph)
341 {
342   gk_graph_t *ngraph;
343 
344   ngraph = gk_graph_Create();
345 
346   ngraph->nvtxs  = graph->nvtxs;
347 
348   /* copy the adjacency structure */
349   if (graph->xadj)
350     ngraph->xadj = gk_zcopy(graph->nvtxs+1, graph->xadj,
351                             gk_zmalloc(graph->nvtxs+1, "gk_graph_Dup: xadj"));
352   if (graph->ivwgts)
353     ngraph->ivwgts = gk_i32copy(graph->nvtxs, graph->ivwgts,
354                             gk_i32malloc(graph->nvtxs, "gk_graph_Dup: ivwgts"));
355   if (graph->ivsizes)
356     ngraph->ivsizes = gk_i32copy(graph->nvtxs, graph->ivsizes,
357                             gk_i32malloc(graph->nvtxs, "gk_graph_Dup: ivsizes"));
358   if (graph->vlabels)
359     ngraph->vlabels = gk_i32copy(graph->nvtxs, graph->vlabels,
360                             gk_i32malloc(graph->nvtxs, "gk_graph_Dup: ivlabels"));
361   if (graph->fvwgts)
362     ngraph->fvwgts = gk_fcopy(graph->nvtxs, graph->fvwgts,
363                             gk_fmalloc(graph->nvtxs, "gk_graph_Dup: fvwgts"));
364   if (graph->fvsizes)
365     ngraph->fvsizes = gk_fcopy(graph->nvtxs, graph->fvsizes,
366                             gk_fmalloc(graph->nvtxs, "gk_graph_Dup: fvsizes"));
367 
368 
369   if (graph->adjncy)
370     ngraph->adjncy = gk_i32copy(graph->xadj[graph->nvtxs], graph->adjncy,
371                             gk_i32malloc(graph->xadj[graph->nvtxs], "gk_graph_Dup: adjncy"));
372   if (graph->iadjwgt)
373     ngraph->iadjwgt = gk_i32copy(graph->xadj[graph->nvtxs], graph->iadjwgt,
374                             gk_i32malloc(graph->xadj[graph->nvtxs], "gk_graph_Dup: iadjwgt"));
375   if (graph->fadjwgt)
376     ngraph->fadjwgt = gk_fcopy(graph->xadj[graph->nvtxs], graph->fadjwgt,
377                             gk_fmalloc(graph->xadj[graph->nvtxs], "gk_graph_Dup: fadjwgt"));
378 
379   return ngraph;
380 }
381 
382 
383 /*************************************************************************/
384 /*! Returns a subgraph containing a set of consecutive vertices.
385     \param graph is the original graph.
386     \param vstart is the starting vertex.
387     \param nvtxs is the number of vertices from vstart to extract.
388     \returns the newly created subgraph.
389 */
390 /**************************************************************************/
gk_graph_ExtractSubgraph(gk_graph_t * graph,int vstart,int nvtxs)391 gk_graph_t *gk_graph_ExtractSubgraph(gk_graph_t *graph, int vstart, int nvtxs)
392 {
393   ssize_t i;
394   gk_graph_t *ngraph;
395 
396   if (vstart+nvtxs > graph->nvtxs)
397     return NULL;
398 
399   ngraph = gk_graph_Create();
400 
401   ngraph->nvtxs  = nvtxs;
402 
403   /* copy the adjancy structure */
404   if (graph->xadj)
405     ngraph->xadj = gk_zcopy(nvtxs+1, graph->xadj+vstart,
406                               gk_zmalloc(nvtxs+1, "gk_graph_ExtractSubgraph: xadj"));
407   for (i=nvtxs; i>=0; i--)
408     ngraph->xadj[i] -= ngraph->xadj[0];
409   ASSERT(ngraph->xadj[0] == 0);
410 
411   if (graph->ivwgts)
412     ngraph->ivwgts = gk_i32copy(nvtxs, graph->ivwgts+vstart,
413                             gk_i32malloc(nvtxs, "gk_graph_ExtractSubgraph: ivwgts"));
414   if (graph->ivsizes)
415     ngraph->ivsizes = gk_i32copy(nvtxs, graph->ivsizes+vstart,
416                             gk_i32malloc(nvtxs, "gk_graph_ExtractSubgraph: ivsizes"));
417   if (graph->vlabels)
418     ngraph->vlabels = gk_i32copy(nvtxs, graph->vlabels+vstart,
419                             gk_i32malloc(nvtxs, "gk_graph_ExtractSubgraph: vlabels"));
420 
421   if (graph->fvwgts)
422     ngraph->fvwgts = gk_fcopy(nvtxs, graph->fvwgts+vstart,
423                             gk_fmalloc(nvtxs, "gk_graph_ExtractSubgraph: fvwgts"));
424   if (graph->fvsizes)
425     ngraph->fvsizes = gk_fcopy(nvtxs, graph->fvsizes+vstart,
426                             gk_fmalloc(nvtxs, "gk_graph_ExtractSubgraph: fvsizes"));
427 
428 
429   ASSERT(ngraph->xadj[nvtxs] == graph->xadj[vstart+nvtxs]-graph->xadj[vstart]);
430   if (graph->adjncy)
431     ngraph->adjncy = gk_i32copy(graph->xadj[vstart+nvtxs]-graph->xadj[vstart],
432                             graph->adjncy+graph->xadj[vstart],
433                             gk_i32malloc(graph->xadj[vstart+nvtxs]-graph->xadj[vstart],
434                                        "gk_graph_ExtractSubgraph: adjncy"));
435   if (graph->iadjwgt)
436     ngraph->iadjwgt = gk_i32copy(graph->xadj[vstart+nvtxs]-graph->xadj[vstart],
437                             graph->iadjwgt+graph->xadj[vstart],
438                             gk_i32malloc(graph->xadj[vstart+nvtxs]-graph->xadj[vstart],
439                                        "gk_graph_ExtractSubgraph: iadjwgt"));
440   if (graph->fadjwgt)
441     ngraph->fadjwgt = gk_fcopy(graph->xadj[vstart+nvtxs]-graph->xadj[vstart],
442                             graph->fadjwgt+graph->xadj[vstart],
443                             gk_fmalloc(graph->xadj[vstart+nvtxs]-graph->xadj[vstart],
444                                        "gk_graph_ExtractSubgraph: fadjwgt"));
445 
446   return ngraph;
447 }
448 
449 
450 /*************************************************************************/
451 /*! Returns a graph that has been reordered according to the permutation.
452     \param[IN] graph is the graph to be re-ordered.
453     \param[IN] perm is the new ordering of the graph's vertices
454     \param[IN] iperm is the original ordering of the re-ordered graph's vertices
455     \returns the newly created copy of the graph.
456 
457     \note Either perm or iperm can be NULL but not both.
458 */
459 /**************************************************************************/
gk_graph_Reorder(gk_graph_t * graph,int32_t * perm,int32_t * iperm)460 gk_graph_t *gk_graph_Reorder(gk_graph_t *graph, int32_t *perm, int32_t *iperm)
461 {
462   ssize_t j, jj, *xadj;
463   int i, k, u, v, nvtxs;
464   int freeperm=0, freeiperm=0;
465   int32_t *adjncy;
466   gk_graph_t *ngraph;
467 
468   if (perm == NULL && iperm == NULL)
469     return NULL;
470 
471   ngraph = gk_graph_Create();
472 
473   ngraph->nvtxs = nvtxs = graph->nvtxs;
474   xadj   = graph->xadj;
475   adjncy = graph->adjncy;
476 
477   /* allocate memory for the different structures that are present in graph */
478   if (graph->xadj)
479     ngraph->xadj = gk_zmalloc(nvtxs+1, "gk_graph_Reorder: xadj");
480 
481   if (graph->ivwgts)
482     ngraph->ivwgts = gk_i32malloc(nvtxs, "gk_graph_Reorder: ivwgts");
483 
484   if (graph->ivsizes)
485     ngraph->ivsizes = gk_i32malloc(nvtxs, "gk_graph_Reorder: ivsizes");
486 
487   if (graph->vlabels)
488     ngraph->vlabels = gk_i32malloc(nvtxs, "gk_graph_Reorder: ivlabels");
489 
490   if (graph->fvwgts)
491     ngraph->fvwgts = gk_fmalloc(nvtxs, "gk_graph_Reorder: fvwgts");
492 
493   if (graph->fvsizes)
494     ngraph->fvsizes = gk_fmalloc(nvtxs, "gk_graph_Reorder: fvsizes");
495 
496 
497   if (graph->adjncy)
498     ngraph->adjncy = gk_i32malloc(graph->xadj[nvtxs], "gk_graph_Reorder: adjncy");
499 
500   if (graph->iadjwgt)
501     ngraph->iadjwgt = gk_i32malloc(graph->xadj[nvtxs], "gk_graph_Reorder: iadjwgt");
502 
503   if (graph->fadjwgt)
504     ngraph->fadjwgt = gk_fmalloc(graph->xadj[nvtxs], "gk_graph_Reorder: fadjwgt");
505 
506 
507   /* create perm/iperm if not provided */
508   if (perm == NULL) {
509     freeperm = 1;
510     perm = gk_i32malloc(nvtxs, "gk_graph_Reorder: perm");
511     for (i=0; i<nvtxs; i++)
512       perm[iperm[i]] = i;
513   }
514   if (iperm == NULL) {
515     freeiperm = 1;
516     iperm = gk_i32malloc(nvtxs, "gk_graph_Reorder: iperm");
517     for (i=0; i<nvtxs; i++)
518       iperm[perm[i]] = i;
519   }
520 
521   /* fill-in the information of the re-ordered graph */
522   ngraph->xadj[0] = jj = 0;
523   for (v=0; v<nvtxs; v++) {
524     u = iperm[v];
525     for (j=xadj[u]; j<xadj[u+1]; j++, jj++) {
526       ngraph->adjncy[jj] = perm[adjncy[j]];
527       if (graph->iadjwgt)
528         ngraph->iadjwgt[jj] = graph->iadjwgt[j];
529       if (graph->fadjwgt)
530         ngraph->fadjwgt[jj] = graph->fadjwgt[j];
531     }
532     if (graph->ivwgts)
533       ngraph->ivwgts[v] = graph->ivwgts[u];
534     if (graph->fvwgts)
535       ngraph->fvwgts[v] = graph->fvwgts[u];
536     if (graph->ivsizes)
537       ngraph->ivsizes[v] = graph->ivsizes[u];
538     if (graph->fvsizes)
539       ngraph->fvsizes[v] = graph->fvsizes[u];
540     if (graph->vlabels)
541       ngraph->vlabels[v] = graph->vlabels[u];
542 
543     ngraph->xadj[v+1] = jj;
544   }
545 
546 
547   /* free memory */
548   if (freeperm)
549     gk_free((void **)&perm, LTERM);
550   if (freeiperm)
551     gk_free((void **)&iperm, LTERM);
552 
553   return ngraph;
554 }
555 
556 
557 /*************************************************************************/
558 /*! This function finds the connected components in a graph.
559 
560     \param graph is the graph structure
561     \param cptr is the ptr structure of the CSR representation of the
562            components. The length of this vector must be graph->nvtxs+1.
563     \param cind is the indices structure of the CSR representation of
564            the components. The length of this vector must be graph->nvtxs.
565 
566     \returns the number of components that it found.
567 
568     \note The cptr and cind parameters can be NULL, in which case only the
569           number of connected components is returned.
570 */
571 /*************************************************************************/
gk_graph_FindComponents(gk_graph_t * graph,int32_t * cptr,int32_t * cind)572 int gk_graph_FindComponents(gk_graph_t *graph, int32_t *cptr, int32_t *cind)
573 {
574   ssize_t i, ii, j, jj, k, nvtxs, first, last, ntodo, ncmps;
575   ssize_t *xadj;
576   int32_t *adjncy, *pos, *todo;
577   int32_t mustfree_ccsr=0, mustfree_where=0;
578 
579   nvtxs  = graph->nvtxs;
580   xadj   = graph->xadj;
581   adjncy = graph->adjncy;
582 
583   /* Deal with NULL supplied cptr/cind vectors */
584   if (cptr == NULL) {
585     cptr = gk_i32malloc(nvtxs+1, "gk_graph_FindComponents: cptr");
586     cind = gk_i32malloc(nvtxs, "gk_graph_FindComponents: cind");
587     mustfree_ccsr = 1;
588   }
589 
590   /* The list of vertices that have not been touched yet.
591      The valid entries are from [0..ntodo). */
592   todo = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_FindComponents: todo"));
593 
594   /* For a vertex that has not been visited, pos[i] is the position in the
595      todo list that this vertex is stored.
596      If a vertex has been visited, pos[i] = -1. */
597   pos = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_FindComponents: pos"));
598 
599 
600   /* Find the connected componends */
601   ncmps = -1;
602   ntodo = nvtxs;     /* All vertices have not been visited */
603   first = last = 0;  /* Point to the first and last vertices that have been touched
604                         but not explored.
605                         These vertices are stored in cind[first]...cind[last-1]. */
606   while (ntodo > 0) {
607     if (first == last) { /* Find another starting vertex */
608       cptr[++ncmps] = first;  /* Mark the end of the current CC */
609 
610       ASSERT(pos[todo[0]] != -1);
611       i = todo[0];
612 
613       cind[last++] = i;
614       pos[i] = -1;
615     }
616 
617     i = cind[first++];  /* Get the first visited but unexplored vertex */
618 
619     /* Remove i from the todo list and put the last item in the todo
620        list at the position that i was so that the todo list will be
621        consequtive. The pos[] array is updated accordingly to keep track
622        the location of the vertices in the todo[] list. */
623     k = pos[i];
624     j = todo[k] = todo[--ntodo];
625     pos[j] = k;
626 
627     for (j=xadj[i]; j<xadj[i+1]; j++) {
628       k = adjncy[j];
629       if (pos[k] != -1) {
630         cind[last++] = k;
631         pos[k] = -1;
632       }
633     }
634   }
635   cptr[++ncmps] = first;
636 
637   if (mustfree_ccsr)
638     gk_free((void **)&cptr, &cind, LTERM);
639 
640   gk_free((void **)&pos, &todo, LTERM);
641 
642   return (int) ncmps;
643 }
644 
645 
646 /*************************************************************************/
647 /*! This function computes a permutation of the vertices based on a
648     breadth-first-traversal. It can be used for re-ordering the graph
649     to reduce its bandwidth for better cache locality.
650     The algorithm used is a simplified version of the method used to find
651     the connected components.
652 
653     \param[IN]  graph is the graph structure
654     \param[IN]  v is the starting vertex of the BFS
655     \param[OUT] perm[i] stores the ID of vertex i in the re-ordered graph.
656     \param[OUT] iperm[i] stores the ID of the vertex that corresponds to
657                 the ith vertex in the re-ordered graph.
658 
659     \note The perm or iperm (but not both) can be NULL, at which point,
660           the corresponding arrays are not returned. Though the program
661           works fine when both are NULL, doing that is not smart.
662           The returned arrays should be freed with gk_free().
663 */
664 /*************************************************************************/
gk_graph_ComputeBFSOrdering(gk_graph_t * graph,int v,int32_t ** r_perm,int32_t ** r_iperm)665 void gk_graph_ComputeBFSOrdering(gk_graph_t *graph, int v, int32_t **r_perm,
666           int32_t **r_iperm)
667 {
668   ssize_t j, *xadj;
669   int i, k, nvtxs, first, last;
670   int32_t *adjncy, *cot, *pos;
671 
672   if (graph->nvtxs <= 0)
673     return;
674 
675   nvtxs  = graph->nvtxs;
676   xadj   = graph->xadj;
677   adjncy = graph->adjncy;
678 
679   /* This array will function like pos + touched of the CC method */
680   pos = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_ComputeBFSOrdering: pos"));
681 
682   /* This array ([C]losed[O]pen[T]odo => cot) serves three purposes.
683      Positions from [0...first) is the current iperm[] vector of the explored vertices;
684      Positions from [first...last) is the OPEN list (i.e., visited vertices);
685      Positions from [last...nvtxs) is the todo list. */
686   cot = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_ComputeBFSOrdering: cot"));
687 
688 
689   /* put v at the front of the todo list */
690   pos[0] = cot[0] = v;
691   pos[v] = cot[v] = 0;
692 
693   /* Find the connected componends induced by the partition */
694   first = last = 0;
695   while (first < nvtxs) {
696     if (first == last) { /* Find another starting vertex */
697       k = cot[last];
698       ASSERT(pos[k] != -1);
699       pos[k] = -1; /* mark node as being visited */
700       last++;
701     }
702 
703     i = cot[first++];  /* the ++ advances the explored vertices */
704     for (j=xadj[i]; j<xadj[i+1]; j++) {
705       k = adjncy[j];
706       /* if a node has already been visited, its perm[] will be -1 */
707       if (pos[k] != -1) {
708         /* pos[k] is the location within iperm of where k resides (it is in the 'todo' part);
709            It is placed in that location cot[last] (end of OPEN list) that we
710            are about to overwrite and update pos[cot[last]] to reflect that. */
711         cot[pos[k]]    = cot[last]; /* put the head of the todo list to
712                                        where k was in the todo list */
713         pos[cot[last]] = pos[k];    /* update perm to reflect the move */
714 
715         cot[last++] = k;  /* put node at the end of the OPEN list */
716         pos[k]      = -1; /* mark node as being visited */
717       }
718     }
719   }
720 
721   /* time to decide what to return */
722   if (r_perm != NULL) {
723     /* use the 'pos' array to build the perm array */
724     for (i=0; i<nvtxs; i++)
725       pos[cot[i]] = i;
726 
727     *r_perm = pos;
728     pos = NULL;
729   }
730 
731   if (r_iperm != NULL) {
732     *r_iperm = cot;
733     cot = NULL;
734   }
735 
736 
737   /* cleanup memory */
738   gk_free((void **)&pos, &cot, LTERM);
739 
740 }
741 
742 
743 /*************************************************************************/
744 /*! This function computes a permutation of the vertices based on a
745     best-first-traversal. It can be used for re-ordering the graph
746     to reduce its bandwidth for better cache locality.
747 
748     \param[IN]  graph is the graph structure.
749     \param[IN]  v is the starting vertex of the best-first traversal.
750     \param[IN]  type indicates the criteria to use to measure the 'bestness'
751                 of a vertex.
752     \param[OUT] perm[i] stores the ID of vertex i in the re-ordered graph.
753     \param[OUT] iperm[i] stores the ID of the vertex that corresponds to
754                 the ith vertex in the re-ordered graph.
755 
756     \note The perm or iperm (but not both) can be NULL, at which point,
757           the corresponding arrays are not returned. Though the program
758           works fine when both are NULL, doing that is not smart.
759           The returned arrays should be freed with gk_free().
760 */
761 /*************************************************************************/
gk_graph_ComputeBestFOrdering0(gk_graph_t * graph,int v,int type,int32_t ** r_perm,int32_t ** r_iperm)762 void gk_graph_ComputeBestFOrdering0(gk_graph_t *graph, int v, int type,
763           int32_t **r_perm, int32_t **r_iperm)
764 {
765   ssize_t j, jj, *xadj;
766   int i, k, u, nvtxs;
767   int32_t *adjncy, *perm, *degrees, *minIDs, *open;
768   gk_i32pq_t *queue;
769 
770   if (graph->nvtxs <= 0)
771     return;
772 
773   nvtxs  = graph->nvtxs;
774   xadj   = graph->xadj;
775   adjncy = graph->adjncy;
776 
777   /* the degree of the vertices in the closed list */
778   degrees = gk_i32smalloc(nvtxs, 0, "gk_graph_ComputeBestFOrdering: degrees");
779 
780   /* the minimum vertex ID of an open vertex to the closed list */
781   minIDs  = gk_i32smalloc(nvtxs, nvtxs+1, "gk_graph_ComputeBestFOrdering: minIDs");
782 
783   /* the open list */
784   open  = gk_i32malloc(nvtxs, "gk_graph_ComputeBestFOrdering: open");
785 
786   /* if perm[i] >= 0, then perm[i] is the order of vertex i;
787      otherwise perm[i] == -1.
788   */
789   perm = gk_i32smalloc(nvtxs, -1, "gk_graph_ComputeBestFOrdering: perm");
790 
791   /* create the queue and put everything in it */
792   queue = gk_i32pqCreate(nvtxs);
793   for (i=0; i<nvtxs; i++)
794     gk_i32pqInsert(queue, i, 0);
795   gk_i32pqUpdate(queue, v, 1);
796 
797   open[0] = v;
798 
799   /* start processing the nodes */
800   for (i=0; i<nvtxs; i++) {
801     if ((v = gk_i32pqGetTop(queue)) == -1)
802       gk_errexit(SIGERR, "The priority queue got empty ahead of time [i=%d].\n", i);
803     if (perm[v] != -1)
804       gk_errexit(SIGERR, "The perm[%d] has already been set.\n", v);
805     perm[v] = i;
806 
807 
808     for (j=xadj[v]; j<xadj[v+1]; j++) {
809       u = adjncy[j];
810       if (perm[u] == -1) {
811         degrees[u]++;
812         minIDs[u] = (i < minIDs[u] ? i : minIDs[u]);
813 
814         switch (type) {
815           case 1: /* DFS */
816             gk_i32pqUpdate(queue, u, 1);
817             break;
818           case 2: /* Max in closed degree */
819             gk_i32pqUpdate(queue, u, degrees[u]);
820             break;
821           case 3: /* Sum of orders in closed list */
822             for (k=0, jj=xadj[u]; jj<xadj[u+1]; jj++) {
823               if (perm[adjncy[jj]] != -1)
824                 k += perm[adjncy[jj]];
825             }
826             gk_i32pqUpdate(queue, u, k);
827             break;
828           case 4: /* Sum of order-differences (w.r.t. current number) in closed
829                      list (updated once in a while) */
830             for (k=0, jj=xadj[u]; jj<xadj[u+1]; jj++) {
831               if (perm[adjncy[jj]] != -1)
832                 k += (i-perm[adjncy[jj]]);
833             }
834             gk_i32pqUpdate(queue, u, k);
835             break;
836           default:
837             ;
838         }
839       }
840     }
841   }
842 
843 
844   /* time to decide what to return */
845   if (r_perm != NULL) {
846     *r_perm = perm;
847     perm = NULL;
848   }
849 
850   if (r_iperm != NULL) {
851     /* use the 'degrees' array to build the iperm array */
852     for (i=0; i<nvtxs; i++)
853       degrees[perm[i]] = i;
854 
855     *r_iperm = degrees;
856     degrees = NULL;
857   }
858 
859 
860 
861   /* cleanup memory */
862   gk_i32pqDestroy(queue);
863   gk_free((void **)&perm, &degrees, &minIDs, &open, LTERM);
864 
865 }
866 
867 
868 /*************************************************************************/
869 /*! This function computes a permutation of the vertices based on a
870     best-first-traversal. It can be used for re-ordering the graph
871     to reduce its bandwidth for better cache locality.
872 
873     \param[IN]  graph is the graph structure.
874     \param[IN]  v is the starting vertex of the best-first traversal.
875     \param[IN]  type indicates the criteria to use to measure the 'bestness'
876                 of a vertex.
877     \param[OUT] perm[i] stores the ID of vertex i in the re-ordered graph.
878     \param[OUT] iperm[i] stores the ID of the vertex that corresponds to
879                 the ith vertex in the re-ordered graph.
880 
881     \note The perm or iperm (but not both) can be NULL, at which point,
882           the corresponding arrays are not returned. Though the program
883           works fine when both are NULL, doing that is not smart.
884           The returned arrays should be freed with gk_free().
885 */
886 /*************************************************************************/
gk_graph_ComputeBestFOrdering(gk_graph_t * graph,int v,int type,int32_t ** r_perm,int32_t ** r_iperm)887 void gk_graph_ComputeBestFOrdering(gk_graph_t *graph, int v, int type,
888           int32_t **r_perm, int32_t **r_iperm)
889 {
890   ssize_t j, jj, *xadj;
891   int i, k, u, nvtxs, nopen, ntodo;
892   int32_t *adjncy, *perm, *degrees, *wdegrees, *sod, *level, *ot, *pos;
893   gk_i32pq_t *queue;
894 
895   if (graph->nvtxs <= 0)
896     return;
897 
898   nvtxs  = graph->nvtxs;
899   xadj   = graph->xadj;
900   adjncy = graph->adjncy;
901 
902   /* the degree of the vertices in the closed list */
903   degrees = gk_i32smalloc(nvtxs, 0, "gk_graph_ComputeBestFOrdering: degrees");
904 
905   /* the weighted degree of the vertices in the closed list for type==3 */
906   wdegrees = gk_i32smalloc(nvtxs, 0, "gk_graph_ComputeBestFOrdering: wdegrees");
907 
908   /* the sum of differences for type==4 */
909   sod = gk_i32smalloc(nvtxs, 0, "gk_graph_ComputeBestFOrdering: sod");
910 
911   /* the encountering level of a vertex type==5 */
912   level = gk_i32smalloc(nvtxs, 0, "gk_graph_ComputeBestFOrdering: level");
913 
914   /* The open+todo list of vertices.
915      The vertices from [0..nopen] are the open vertices.
916      The vertices from [nopen..ntodo) are the todo vertices.
917      */
918   ot = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_FindComponents: ot"));
919 
920   /* For a vertex that has not been explored, pos[i] is the position in the ot list. */
921   pos = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_FindComponents: pos"));
922 
923   /* if perm[i] >= 0, then perm[i] is the order of vertex i; otherwise perm[i] == -1. */
924   perm = gk_i32smalloc(nvtxs, -1, "gk_graph_ComputeBestFOrdering: perm");
925 
926   /* create the queue and put the starting vertex in it */
927   queue = gk_i32pqCreate(nvtxs);
928   gk_i32pqInsert(queue, v, 1);
929 
930   /* put v at the front of the open list */
931   pos[0] = ot[0] = v;
932   pos[v] = ot[v] = 0;
933   nopen = 1;
934   ntodo = nvtxs;
935 
936   /* start processing the nodes */
937   for (i=0; i<nvtxs; i++) {
938     if (nopen == 0) { /* deal with non-connected graphs */
939       gk_i32pqInsert(queue, ot[0], 1);
940       nopen++;
941     }
942 
943     if ((v = gk_i32pqGetTop(queue)) == -1)
944       gk_errexit(SIGERR, "The priority queue got empty ahead of time [i=%d].\n", i);
945 
946     if (perm[v] != -1)
947       gk_errexit(SIGERR, "The perm[%d] has already been set.\n", v);
948     perm[v] = i;
949 
950     if (ot[pos[v]] != v)
951       gk_errexit(SIGERR, "Something went wrong [ot[pos[%d]]!=%d.\n", v, v);
952     if (pos[v] >= nopen)
953       gk_errexit(SIGERR, "The position of v is not in open list. pos[%d]=%d is >=%d.\n", v, pos[v], nopen);
954 
955     /* remove v from the open list and re-arrange the todo part of the list */
956     ot[pos[v]]       = ot[nopen-1];
957     pos[ot[nopen-1]] = pos[v];
958     if (ntodo > nopen) {
959       ot[nopen-1]      = ot[ntodo-1];
960       pos[ot[ntodo-1]] = nopen-1;
961     }
962     nopen--;
963     ntodo--;
964 
965     for (j=xadj[v]; j<xadj[v+1]; j++) {
966       u = adjncy[j];
967       if (perm[u] == -1) {
968         /* update ot list, if u is not in the open list by putting it at the end
969            of the open list. */
970         if (degrees[u] == 0) {
971           ot[pos[u]]     = ot[nopen];
972           pos[ot[nopen]] = pos[u];
973           ot[nopen]      = u;
974           pos[u]         = nopen;
975           nopen++;
976 
977           level[u] = level[v]+1;
978           gk_i32pqInsert(queue, u, 0);
979         }
980 
981 
982         /* update the in-closed degree */
983         degrees[u]++;
984 
985         /* update the queues based on the type */
986         switch (type) {
987           case 1: /* DFS */
988             gk_i32pqUpdate(queue, u, 1000*(i+1)+degrees[u]);
989             break;
990 
991           case 2: /* Max in closed degree */
992             gk_i32pqUpdate(queue, u, degrees[u]);
993             break;
994 
995           case 3: /* Sum of orders in closed list */
996             wdegrees[u] += i;
997             gk_i32pqUpdate(queue, u, wdegrees[u]);
998             break;
999 
1000           case 4: /* Sum of order-differences */
1001             /* this is handled at the end of the loop */
1002             ;
1003             break;
1004 
1005           case 5: /* BFS with in degree priority */
1006             gk_i32pqUpdate(queue, u, -(1000*level[u] - degrees[u]));
1007             break;
1008 
1009           case 6: /* Hybrid of 1+2 */
1010             gk_i32pqUpdate(queue, u, (i+1)*degrees[u]);
1011             break;
1012 
1013           default:
1014             ;
1015         }
1016       }
1017     }
1018 
1019     if (type == 4) { /* update all the vertices in the open list */
1020       for (j=0; j<nopen; j++) {
1021         u = ot[j];
1022         if (perm[u] != -1)
1023           gk_errexit(SIGERR, "For i=%d, the open list contains a closed vertex: ot[%zd]=%d, perm[%d]=%d.\n", i, j, u, u, perm[u]);
1024         sod[u] += degrees[u];
1025         if (i<1000 || i%25==0)
1026           gk_i32pqUpdate(queue, u, sod[u]);
1027       }
1028     }
1029 
1030     /*
1031     for (j=0; j<ntodo; j++) {
1032       if (pos[ot[j]] != j)
1033         gk_errexit(SIGERR, "pos[ot[%zd]] != %zd.\n", j, j);
1034     }
1035     */
1036 
1037   }
1038 
1039 
1040   /* time to decide what to return */
1041   if (r_perm != NULL) {
1042     *r_perm = perm;
1043     perm = NULL;
1044   }
1045 
1046   if (r_iperm != NULL) {
1047     /* use the 'degrees' array to build the iperm array */
1048     for (i=0; i<nvtxs; i++)
1049       degrees[perm[i]] = i;
1050 
1051     *r_iperm = degrees;
1052     degrees = NULL;
1053   }
1054 
1055 
1056 
1057   /* cleanup memory */
1058   gk_i32pqDestroy(queue);
1059   gk_free((void **)&perm, &degrees, &wdegrees, &sod, &ot, &pos, &level, LTERM);
1060 
1061 }
1062 
1063 
1064 /*************************************************************************/
1065 /*! This function computes the single-source shortest path lengths from the
1066     root node to all the other nodes in the graph. If the graph is not
1067     connected then, the sortest part to the vertices in the other components
1068     is -1.
1069 
1070     \param[IN]  graph is the graph structure.
1071     \param[IN]  v is the root of the single-source shortest path computations.
1072     \param[IN]  type indicates the criteria to use to measure the 'bestness'
1073                 of a vertex.
1074     \param[OUT] sps[i] stores the length of the shortest path from v to vertex i.
1075                 If no such path exists, then it is -1. Note that the returned
1076                 array will be either an array of int32_t or an array of floats.
1077                 The specific type is determined by the existance of non NULL
1078                 iadjwgt and fadjwgt arrays. If both of these arrays exist, then
1079                 priority is given to iadjwgt.
1080 
1081     \note The returned array should be freed with gk_free().
1082 */
1083 /*************************************************************************/
gk_graph_SingleSourceShortestPaths(gk_graph_t * graph,int v,void ** r_sps)1084 void gk_graph_SingleSourceShortestPaths(gk_graph_t *graph, int v, void **r_sps)
1085 {
1086   ssize_t *xadj;
1087   int i, u, nvtxs;
1088   int32_t *adjncy, *inqueue;
1089 
1090   if (graph->nvtxs <= 0)
1091     return;
1092 
1093   nvtxs  = graph->nvtxs;
1094   xadj   = graph->xadj;
1095   adjncy = graph->adjncy;
1096 
1097   inqueue = gk_i32smalloc(nvtxs, 0, "gk_graph_SingleSourceShortestPaths: inqueue");
1098 
1099   /* determine if you will be computing using int32_t or float and proceed from there */
1100   if (graph->iadjwgt != NULL) {
1101     gk_i32pq_t *queue;
1102     int32_t *adjwgt;
1103     int32_t *sps;
1104 
1105     adjwgt = graph->iadjwgt;
1106 
1107     queue = gk_i32pqCreate(nvtxs);
1108     gk_i32pqInsert(queue, v, 0);
1109     inqueue[v] = 1;
1110 
1111     sps = gk_i32smalloc(nvtxs, -1, "gk_graph_SingleSourceShortestPaths: sps");
1112     sps[v] = 0;
1113 
1114     /* start processing the nodes */
1115     while ((v = gk_i32pqGetTop(queue)) != -1) {
1116       inqueue[v] = 2;
1117 
1118       /* relax the adjacent edges */
1119       for (i=xadj[v]; i<xadj[v+1]; i++) {
1120         u = adjncy[i];
1121         if (inqueue[u] == 2)
1122           continue;
1123 
1124         if (sps[u] < 0 || sps[v]+adjwgt[i] < sps[u]) {
1125           sps[u] = sps[v]+adjwgt[i];
1126 
1127           if (inqueue[u])
1128             gk_i32pqUpdate(queue, u, -sps[u]);
1129           else {
1130             gk_i32pqInsert(queue, u, -sps[u]);
1131             inqueue[u] = 1;
1132           }
1133         }
1134       }
1135     }
1136 
1137     *r_sps = (void *)sps;
1138 
1139     gk_i32pqDestroy(queue);
1140   }
1141   else {
1142     gk_fpq_t *queue;
1143     float *adjwgt;
1144     float *sps;
1145 
1146     adjwgt = graph->fadjwgt;
1147 
1148     queue = gk_fpqCreate(nvtxs);
1149     gk_fpqInsert(queue, v, 0);
1150     inqueue[v] = 1;
1151 
1152     sps = gk_fsmalloc(nvtxs, -1, "gk_graph_SingleSourceShortestPaths: sps");
1153     sps[v] = 0;
1154 
1155     /* start processing the nodes */
1156     while ((v = gk_fpqGetTop(queue)) != -1) {
1157       inqueue[v] = 2;
1158 
1159       /* relax the adjacent edges */
1160       for (i=xadj[v]; i<xadj[v+1]; i++) {
1161         u = adjncy[i];
1162         if (inqueue[u] == 2)
1163           continue;
1164 
1165         if (sps[u] < 0 || sps[v]+adjwgt[i] < sps[u]) {
1166           sps[u] = sps[v]+adjwgt[i];
1167 
1168           if (inqueue[u])
1169             gk_fpqUpdate(queue, u, -sps[u]);
1170           else {
1171             gk_fpqInsert(queue, u, -sps[u]);
1172             inqueue[u] = 1;
1173           }
1174         }
1175       }
1176     }
1177 
1178     *r_sps = (void *)sps;
1179 
1180     gk_fpqDestroy(queue);
1181   }
1182 
1183   gk_free((void **)&inqueue, LTERM);
1184 
1185 }
1186 
1187 
1188 
1189 #ifdef XXX
1190 
1191 /*************************************************************************/
1192 /*! Sorts the adjacency lists in increasing vertex order
1193     \param graph the graph itself,
1194 */
1195 /**************************************************************************/
gk_graph_SortAdjacencies(gk_graph_t * graph)1196 void gk_graph_SortAdjacencies(gk_graph_t *graph)
1197 {
1198   int n, nn=0;
1199   ssize_t *ptr;
1200   int *ind;
1201   float *val;
1202 
1203   switch (what) {
1204     case GK_CSR_ROW:
1205       if (!graph->rowptr)
1206         gk_errexit(SIGERR, "Row-based view of the graphrix does not exists.\n");
1207 
1208       n   = graph->nrows;
1209       ptr = graph->rowptr;
1210       ind = graph->rowind;
1211       val = graph->rowval;
1212       break;
1213 
1214     case GK_CSR_COL:
1215       if (!graph->colptr)
1216         gk_errexit(SIGERR, "Column-based view of the graphrix does not exists.\n");
1217 
1218       n   = graph->ncols;
1219       ptr = graph->colptr;
1220       ind = graph->colind;
1221       val = graph->colval;
1222       break;
1223 
1224     default:
1225       gk_errexit(SIGERR, "Invalid index type of %d.\n", what);
1226       return;
1227   }
1228 
1229   #pragma omp parallel if (n > 100)
1230   {
1231     ssize_t i, j, k;
1232     gk_ikv_t *cand;
1233     float *tval;
1234 
1235     #pragma omp single
1236     for (i=0; i<n; i++)
1237       nn = gk_max(nn, ptr[i+1]-ptr[i]);
1238 
1239     cand = gk_ikvmalloc(nn, "gk_graph_SortIndices: cand");
1240     tval = gk_fmalloc(nn, "gk_graph_SortIndices: tval");
1241 
1242     #pragma omp for schedule(static)
1243     for (i=0; i<n; i++) {
1244       for (k=0, j=ptr[i]; j<ptr[i+1]; j++) {
1245         if (j > ptr[i] && ind[j] < ind[j-1])
1246           k = 1; /* an inversion */
1247         cand[j-ptr[i]].val = j-ptr[i];
1248         cand[j-ptr[i]].key = ind[j];
1249         tval[j-ptr[i]]     = val[j];
1250       }
1251       if (k) {
1252         gk_ikvsorti(ptr[i+1]-ptr[i], cand);
1253         for (j=ptr[i]; j<ptr[i+1]; j++) {
1254           ind[j] = cand[j-ptr[i]].key;
1255           val[j] = tval[cand[j-ptr[i]].val];
1256         }
1257       }
1258     }
1259 
1260     gk_free((void **)&cand, &tval, LTERM);
1261   }
1262 
1263 }
1264 
1265 
1266 /*************************************************************************/
1267 /*! Returns a subgraphrix containing a certain set of rows.
1268     \param graph is the original graphrix.
1269     \param nrows is the number of rows to extract.
1270     \param rind is the set of row numbers to extract.
1271     \returns the row structure of the newly created subgraphrix.
1272 */
1273 /**************************************************************************/
gk_graph_ExtractRows(gk_graph_t * graph,int nrows,int * rind)1274 gk_graph_t *gk_graph_ExtractRows(gk_graph_t *graph, int nrows, int *rind)
1275 {
1276   ssize_t i, ii, j, nnz;
1277   gk_graph_t *ngraph;
1278 
1279   ngraph = gk_graph_Create();
1280 
1281   ngraph->nrows = nrows;
1282   ngraph->ncols = graph->ncols;
1283 
1284   for (nnz=0, i=0; i<nrows; i++)
1285     nnz += graph->rowptr[rind[i]+1]-graph->rowptr[rind[i]];
1286 
1287   ngraph->rowptr = gk_zmalloc(ngraph->nrows+1, "gk_graph_ExtractPartition: rowptr");
1288   ngraph->rowind = gk_imalloc(nnz, "gk_graph_ExtractPartition: rowind");
1289   ngraph->rowval = gk_fmalloc(nnz, "gk_graph_ExtractPartition: rowval");
1290 
1291   ngraph->rowptr[0] = 0;
1292   for (nnz=0, j=0, ii=0; ii<nrows; ii++) {
1293     i = rind[ii];
1294     gk_icopy(graph->rowptr[i+1]-graph->rowptr[i], graph->rowind+graph->rowptr[i], ngraph->rowind+nnz);
1295     gk_fcopy(graph->rowptr[i+1]-graph->rowptr[i], graph->rowval+graph->rowptr[i], ngraph->rowval+nnz);
1296     nnz += graph->rowptr[i+1]-graph->rowptr[i];
1297     ngraph->rowptr[++j] = nnz;
1298   }
1299   ASSERT(j == ngraph->nrows);
1300 
1301   return ngraph;
1302 }
1303 
1304 
1305 /*************************************************************************/
1306 /*! Returns a subgraphrix corresponding to a specified partitioning of rows.
1307     \param graph is the original graphrix.
1308     \param part is the partitioning vector of the rows.
1309     \param pid is the partition ID that will be extracted.
1310     \returns the row structure of the newly created subgraphrix.
1311 */
1312 /**************************************************************************/
gk_graph_ExtractPartition(gk_graph_t * graph,int * part,int pid)1313 gk_graph_t *gk_graph_ExtractPartition(gk_graph_t *graph, int *part, int pid)
1314 {
1315   ssize_t i, j, nnz;
1316   gk_graph_t *ngraph;
1317 
1318   ngraph = gk_graph_Create();
1319 
1320   ngraph->nrows = 0;
1321   ngraph->ncols = graph->ncols;
1322 
1323   for (nnz=0, i=0; i<graph->nrows; i++) {
1324     if (part[i] == pid) {
1325       ngraph->nrows++;
1326       nnz += graph->rowptr[i+1]-graph->rowptr[i];
1327     }
1328   }
1329 
1330   ngraph->rowptr = gk_zmalloc(ngraph->nrows+1, "gk_graph_ExtractPartition: rowptr");
1331   ngraph->rowind = gk_imalloc(nnz, "gk_graph_ExtractPartition: rowind");
1332   ngraph->rowval = gk_fmalloc(nnz, "gk_graph_ExtractPartition: rowval");
1333 
1334   ngraph->rowptr[0] = 0;
1335   for (nnz=0, j=0, i=0; i<graph->nrows; i++) {
1336     if (part[i] == pid) {
1337       gk_icopy(graph->rowptr[i+1]-graph->rowptr[i], graph->rowind+graph->rowptr[i], ngraph->rowind+nnz);
1338       gk_fcopy(graph->rowptr[i+1]-graph->rowptr[i], graph->rowval+graph->rowptr[i], ngraph->rowval+nnz);
1339       nnz += graph->rowptr[i+1]-graph->rowptr[i];
1340       ngraph->rowptr[++j] = nnz;
1341     }
1342   }
1343   ASSERT(j == ngraph->nrows);
1344 
1345   return ngraph;
1346 }
1347 
1348 
1349 /*************************************************************************/
1350 /*! Splits the graphrix into multiple sub-graphrices based on the provided
1351     color array.
1352     \param graph is the original graphrix.
1353     \param color is an array of size equal to the number of non-zeros
1354            in the graphrix (row-wise structure). The graphrix is split into
1355            as many parts as the number of colors. For meaningfull results,
1356            the colors should be numbered consecutively starting from 0.
1357     \returns an array of graphrices for each supplied color number.
1358 */
1359 /**************************************************************************/
gk_graph_Split(gk_graph_t * graph,int * color)1360 gk_graph_t **gk_graph_Split(gk_graph_t *graph, int *color)
1361 {
1362   ssize_t i, j;
1363   int nrows, ncolors;
1364   ssize_t *rowptr;
1365   int *rowind;
1366   float *rowval;
1367   gk_graph_t **sgraphs;
1368 
1369   nrows  = graph->nrows;
1370   rowptr = graph->rowptr;
1371   rowind = graph->rowind;
1372   rowval = graph->rowval;
1373 
1374   ncolors = gk_imax(rowptr[nrows], color)+1;
1375 
1376   sgraphs = (gk_graph_t **)gk_malloc(sizeof(gk_graph_t *)*ncolors, "gk_graph_Split: sgraphs");
1377   for (i=0; i<ncolors; i++) {
1378     sgraphs[i] = gk_graph_Create();
1379     sgraphs[i]->nrows  = graph->nrows;
1380     sgraphs[i]->ncols  = graph->ncols;
1381     sgraphs[i]->rowptr = gk_zsmalloc(nrows+1, 0, "gk_graph_Split: sgraphs[i]->rowptr");
1382   }
1383 
1384   for (i=0; i<nrows; i++) {
1385     for (j=rowptr[i]; j<rowptr[i+1]; j++)
1386       sgraphs[color[j]]->rowptr[i]++;
1387   }
1388   for (i=0; i<ncolors; i++)
1389     MAKECSR(j, nrows, sgraphs[i]->rowptr);
1390 
1391   for (i=0; i<ncolors; i++) {
1392     sgraphs[i]->rowind = gk_imalloc(sgraphs[i]->rowptr[nrows], "gk_graph_Split: sgraphs[i]->rowind");
1393     sgraphs[i]->rowval = gk_fmalloc(sgraphs[i]->rowptr[nrows], "gk_graph_Split: sgraphs[i]->rowval");
1394   }
1395 
1396   for (i=0; i<nrows; i++) {
1397     for (j=rowptr[i]; j<rowptr[i+1]; j++) {
1398       sgraphs[color[j]]->rowind[sgraphs[color[j]]->rowptr[i]] = rowind[j];
1399       sgraphs[color[j]]->rowval[sgraphs[color[j]]->rowptr[i]] = rowval[j];
1400       sgraphs[color[j]]->rowptr[i]++;
1401     }
1402   }
1403 
1404   for (i=0; i<ncolors; i++)
1405     SHIFTCSR(j, nrows, sgraphs[i]->rowptr);
1406 
1407   return sgraphs;
1408 }
1409 
1410 
1411 /*************************************************************************/
1412 /*! Prunes certain rows/columns of the graphrix. The prunning takes place
1413     by analyzing the row structure of the graphrix. The prunning takes place
1414     by removing rows/columns but it does not affect the numbering of the
1415     remaining rows/columns.
1416 
1417     \param graph the graphrix to be prunned,
1418     \param what indicates if the rows (GK_CSR_ROW) or the columns (GK_CSR_COL)
1419            of the graphrix will be prunned,
1420     \param minf is the minimum number of rows (columns) that a column (row) must
1421            be present in order to be kept,
1422     \param maxf is the maximum number of rows (columns) that a column (row) must
1423           be present at in order to be kept.
1424     \returns the prunned graphrix consisting only of its row-based structure.
1425           The input graphrix is not modified.
1426 */
1427 /**************************************************************************/
gk_graph_Prune(gk_graph_t * graph,int what,int minf,int maxf)1428 gk_graph_t *gk_graph_Prune(gk_graph_t *graph, int what, int minf, int maxf)
1429 {
1430   ssize_t i, j, nnz;
1431   int nrows, ncols;
1432   ssize_t *rowptr, *nrowptr;
1433   int *rowind, *nrowind, *collen;
1434   float *rowval, *nrowval;
1435   gk_graph_t *ngraph;
1436 
1437   ngraph = gk_graph_Create();
1438 
1439   nrows = ngraph->nrows = graph->nrows;
1440   ncols = ngraph->ncols = graph->ncols;
1441 
1442   rowptr = graph->rowptr;
1443   rowind = graph->rowind;
1444   rowval = graph->rowval;
1445 
1446   nrowptr = ngraph->rowptr = gk_zmalloc(nrows+1, "gk_graph_Prune: nrowptr");
1447   nrowind = ngraph->rowind = gk_imalloc(rowptr[nrows], "gk_graph_Prune: nrowind");
1448   nrowval = ngraph->rowval = gk_fmalloc(rowptr[nrows], "gk_graph_Prune: nrowval");
1449 
1450 
1451   switch (what) {
1452     case GK_CSR_COL:
1453       collen = gk_ismalloc(ncols, 0, "gk_graph_Prune: collen");
1454 
1455       for (i=0; i<nrows; i++) {
1456         for (j=rowptr[i]; j<rowptr[i+1]; j++) {
1457           ASSERT(rowind[j] < ncols);
1458           collen[rowind[j]]++;
1459         }
1460       }
1461       for (i=0; i<ncols; i++)
1462         collen[i] = (collen[i] >= minf && collen[i] <= maxf ? 1 : 0);
1463 
1464       nrowptr[0] = 0;
1465       for (nnz=0, i=0; i<nrows; i++) {
1466         for (j=rowptr[i]; j<rowptr[i+1]; j++) {
1467           if (collen[rowind[j]]) {
1468             nrowind[nnz] = rowind[j];
1469             nrowval[nnz] = rowval[j];
1470             nnz++;
1471           }
1472         }
1473         nrowptr[i+1] = nnz;
1474       }
1475       gk_free((void **)&collen, LTERM);
1476       break;
1477 
1478     case GK_CSR_ROW:
1479       nrowptr[0] = 0;
1480       for (nnz=0, i=0; i<nrows; i++) {
1481         if (rowptr[i+1]-rowptr[i] >= minf && rowptr[i+1]-rowptr[i] <= maxf) {
1482           for (j=rowptr[i]; j<rowptr[i+1]; j++, nnz++) {
1483             nrowind[nnz] = rowind[j];
1484             nrowval[nnz] = rowval[j];
1485           }
1486         }
1487         nrowptr[i+1] = nnz;
1488       }
1489       break;
1490 
1491     default:
1492       gk_graph_Free(&ngraph);
1493       gk_errexit(SIGERR, "Unknown prunning type of %d\n", what);
1494       return NULL;
1495   }
1496 
1497   return ngraph;
1498 }
1499 
1500 
1501 
1502 /*************************************************************************/
1503 /*! Normalizes the rows/columns of the graphrix to be unit
1504     length.
1505     \param graph the graphrix itself,
1506     \param what indicates what will be normalized and is obtained by
1507            specifying GK_CSR_ROW, GK_CSR_COL, GK_CSR_ROW|GK_CSR_COL.
1508     \param norm indicates what norm is to normalize to, 1: 1-norm, 2: 2-norm
1509 */
1510 /**************************************************************************/
gk_graph_Normalize(gk_graph_t * graph,int what,int norm)1511 void gk_graph_Normalize(gk_graph_t *graph, int what, int norm)
1512 {
1513   ssize_t i, j;
1514   int n;
1515   ssize_t *ptr;
1516   float *val, sum;
1517 
1518   if (what&GK_CSR_ROW && graph->rowval) {
1519     n   = graph->nrows;
1520     ptr = graph->rowptr;
1521     val = graph->rowval;
1522 
1523     #pragma omp parallel if (ptr[n] > OMPMINOPS)
1524     {
1525       #pragma omp for private(j,sum) schedule(static)
1526       for (i=0; i<n; i++) {
1527         for (sum=0.0, j=ptr[i]; j<ptr[i+1]; j++){
1528   	if (norm == 2)
1529   	  sum += val[j]*val[j];
1530   	else if (norm == 1)
1531   	  sum += val[j]; /* assume val[j] > 0 */
1532         }
1533         if (sum > 0) {
1534   	if (norm == 2)
1535   	  sum=1.0/sqrt(sum);
1536   	else if (norm == 1)
1537   	  sum=1.0/sum;
1538           for (j=ptr[i]; j<ptr[i+1]; j++)
1539             val[j] *= sum;
1540 
1541         }
1542       }
1543     }
1544   }
1545 
1546   if (what&GK_CSR_COL && graph->colval) {
1547     n   = graph->ncols;
1548     ptr = graph->colptr;
1549     val = graph->colval;
1550 
1551     #pragma omp parallel if (ptr[n] > OMPMINOPS)
1552     {
1553     #pragma omp for private(j,sum) schedule(static)
1554       for (i=0; i<n; i++) {
1555         for (sum=0.0, j=ptr[i]; j<ptr[i+1]; j++)
1556   	if (norm == 2)
1557   	  sum += val[j]*val[j];
1558   	else if (norm == 1)
1559   	  sum += val[j];
1560         if (sum > 0) {
1561   	if (norm == 2)
1562   	  sum=1.0/sqrt(sum);
1563   	else if (norm == 1)
1564   	  sum=1.0/sum;
1565           for (j=ptr[i]; j<ptr[i+1]; j++)
1566             val[j] *= sum;
1567         }
1568       }
1569     }
1570   }
1571 }
1572 
1573 
1574 #endif
1575