1 /* vim:set shiftwidth=4 ts=8: */
2
3 /*************************************************************************
4 * Copyright (c) 2011 AT&T Intellectual Property
5 * All rights reserved. This program and the accompanying materials
6 * are made available under the terms of the Eclipse Public License v1.0
7 * which accompanies this distribution, and is available at
8 * http://www.eclipse.org/legal/epl-v10.html
9 *
10 * Contributors: See CVS logs. Details at http://www.graphviz.org/
11 *************************************************************************/
12
13 ///////////////////////////////////////
14 // //
15 // This file contains the functions //
16 // for constructing and managing the //
17 // hierarchy structure //
18 // //
19 ///////////////////////////////////////
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <math.h>
25 #include <time.h>
26 #include <assert.h>
27 #include "memory.h"
28 #include "arith.h"
29 #include "hierarchy.h"
30
31 static int cur_level = 0;
32
33 /////////////////////////
34 // Some utilities for //
35 // 'maxmatch(..)' //
36 /////////////////////////
37
38 static double
unweighted_common_fraction(v_data * graph,int v,int u,float * v_vector)39 unweighted_common_fraction(v_data * graph, int v, int u, float *v_vector)
40 {
41 // returns: |N(v) & N(u)| / |N(v) or N(u)|
42 // v_vector[i]>0 <==> i is neighbor of v or is v itself
43
44 int neighbor;
45 int num_shared_neighbors = 0;
46 int j;
47 for (j = 0; j < graph[u].nedges; j++) {
48 neighbor = graph[u].edges[j];
49 if (v_vector[neighbor] > 0) {
50 // a shared neighobr
51 num_shared_neighbors++;
52 }
53 }
54 // parallel to the weighted version:
55 //return 2*num_shared_neighbors/(graph[v].nedges+graph[u].nedges);
56
57 // more natural
58 return ((double) num_shared_neighbors) / (graph[v].nedges +
59 graph[u].nedges -
60 num_shared_neighbors);
61 }
62
fill_neighbors_vec(v_data * graph,int vtx,float * vtx_vec)63 static float fill_neighbors_vec(v_data * graph, int vtx, float *vtx_vec)
64 {
65 float sum_weights = 0;
66 int j;
67 if (graph[0].ewgts != NULL) {
68 for (j = 0; j < graph[vtx].nedges; j++) {
69 sum_weights += (vtx_vec[graph[vtx].edges[j]] = (float) fabs(graph[vtx].ewgts[j])); // use fabs for the self loop
70 }
71 } else {
72 for (j = 0; j < graph[vtx].nedges; j++) {
73 sum_weights += (vtx_vec[graph[vtx].edges[j]] = 1);
74 }
75 }
76 return sum_weights;
77 }
78
79 static void
fill_neighbors_vec_unweighted(v_data * graph,int vtx,float * vtx_vec)80 fill_neighbors_vec_unweighted(v_data * graph, int vtx, float *vtx_vec)
81 {
82 // a node is a neighbor of itself!
83 int j;
84 for (j = 0; j < graph[vtx].nedges; j++) {
85 vtx_vec[graph[vtx].edges[j]] = 1;
86 }
87 }
88
empty_neighbors_vec(v_data * graph,int vtx,float * vtx_vec)89 static void empty_neighbors_vec(v_data * graph, int vtx, float *vtx_vec)
90 {
91 int j;
92 for (j = 0; j < graph[vtx].nedges; j++) {
93 vtx_vec[graph[vtx].edges[j]] = 0;
94 }
95 }
96
97
dist3(v_data * graph,int node1,int node2)98 static int dist3(v_data * graph, int node1, int node2)
99 {
100 // succeeds if the graph theoretic distance between the nodes is no more than 3
101 int i, j, k;
102 int u, v;
103 for (i = 1; i < graph[node1].nedges; i++) {
104 u = graph[node1].edges[i];
105 if (u == node2) {
106 return 1;
107 }
108 for (j = 1; j < graph[u].nedges; j++) {
109 v = graph[u].edges[j];
110 if (v == node2) {
111 return 1;
112 }
113 for (k = 1; k < graph[v].nedges; k++) {
114 if (graph[v].edges[k] == node2) {
115 return 1;
116 }
117 }
118 }
119 }
120 return 0;
121 }
122
123 #define A 1.0
124 #define B 1.0
125 #define C 3.0
126 #define D 1.0
127
ddist(ex_vtx_data * geom_graph,int v,int u)128 static double ddist(ex_vtx_data * geom_graph, int v, int u)
129 {
130 // Euclidean distance between nodes 'v' and 'u'
131 double x_v = geom_graph[v].x_coord, y_v = geom_graph[v].y_coord,
132 x_u = geom_graph[u].x_coord, y_u = geom_graph[u].y_coord;
133
134 return sqrt((x_v - x_u) * (x_v - x_u) + (y_v - y_u) * (y_v - y_u));
135 }
136
137 extern void quicksort_place(double *, int *, int first, int last);
138
139 static int
maxmatch(v_data * graph,ex_vtx_data * geom_graph,int nvtxs,int * mflag,int dist2_limit)140 maxmatch(v_data * graph, /* array of vtx data for graph */
141 ex_vtx_data * geom_graph, /* array of vtx data for graph */
142 int nvtxs, /* number of vertices in graph */
143 int *mflag, /* flag indicating vtx selected or not */
144 int dist2_limit
145 )
146 /*
147 Compute a matching of the nodes set.
148 The matching is not based only on the edge list of 'graph',
149 which might be too small,
150 but on the wider edge list of 'geom_graph' (which includes 'graph''s edges)
151
152 We match nodes that are close both in the graph-theoretical sense and
153 in the geometry sense (in the layout)
154 */
155 {
156 int *order; /* random ordering of vertices */
157 int *iptr, *jptr; /* loops through integer arrays */
158 int vtx; /* vertex to process next */
159 int neighbor; /* neighbor of a vertex */
160 int nmerged = 0; /* number of edges in matching */
161 int i, j; /* loop counters */
162 float max_norm_edge_weight;
163 double inv_size;
164 double *matchability = N_NEW(nvtxs, double);
165 double min_edge_len;
166 double closest_val = -1, val;
167 int closest_neighbor;
168 float *vtx_vec = N_NEW(nvtxs, float);
169 float *weighted_vtx_vec = N_NEW(nvtxs, float);
170 float sum_weights;
171
172 // gather statistics, to enable normalizing the values
173 double avg_edge_len = 0, avg_deg_2 = 0;
174 int nedges = 0;
175
176 for (i = 0; i < nvtxs; i++) {
177 avg_deg_2 += graph[i].nedges;
178 for (j = 1; j < graph[i].nedges; j++) {
179 avg_edge_len += ddist(geom_graph, i, graph[i].edges[j]);
180 nedges++;
181 }
182 }
183 avg_edge_len /= nedges;
184 avg_deg_2 /= nvtxs;
185 avg_deg_2 *= avg_deg_2;
186
187 // the normalized edge weight of edge <v,u> is defined as:
188 // weight(<v,u>)/sqrt(size(v)*size(u))
189 // Now we compute the maximal normalized weight
190 if (graph[0].ewgts != NULL) {
191 max_norm_edge_weight = -1;
192 for (i = 0; i < nvtxs; i++) {
193 inv_size = sqrt(1.0 / geom_graph[i].size);
194 for (j = 1; j < graph[i].nedges; j++) {
195 if (graph[i].ewgts[j] * inv_size /
196 sqrt((float) geom_graph[graph[i].edges[j]].size) >
197 max_norm_edge_weight) {
198 max_norm_edge_weight =
199 (float) (graph[i].ewgts[j] * inv_size /
200 sqrt((double)
201 geom_graph[graph[i].edges[j]].size));
202 }
203 }
204 }
205 } else {
206 max_norm_edge_weight = 1;
207 }
208
209 /* Now determine the order of the vertices. */
210 iptr = order = N_NEW(nvtxs, int);
211 jptr = mflag;
212 for (i = 0; i < nvtxs; i++) {
213 *(iptr++) = i;
214 *(jptr++) = -1;
215 }
216
217 // Option 1: random permutation
218 #if 0
219 int temp;
220 for (i=0; i<nvtxs-1; i++) {
221 // use long_rand() (not rand()), as n may be greater than RAND_MAX
222 j=i+long_rand()%(nvtxs-i);
223 temp=order[i];
224 order[i]=order[j];
225 order[j]=temp;
226 }
227 #endif
228 // Option 2: sort the nodes beginning with the ones highly approriate for matching
229
230 #ifdef DEBUG
231 srand(0);
232 #endif
233 for (i = 0; i < nvtxs; i++) {
234 vtx = order[i];
235 matchability[vtx] = graph[vtx].nedges; // we less want to match high degree nodes
236 matchability[vtx] += geom_graph[vtx].size; // we less want to match large sized nodes
237 min_edge_len = 1e99;
238 for (j = 1; j < graph[vtx].nedges; j++) {
239 min_edge_len =
240 MIN(min_edge_len,
241 ddist(geom_graph, vtx,
242 graph[vtx].edges[j]) / avg_edge_len);
243 }
244 matchability[vtx] += min_edge_len; // we less want to match distant nodes
245 matchability[vtx] += ((double) rand()) / RAND_MAX; // add some randomness
246 }
247 quicksort_place(matchability, order, 0, nvtxs - 1);
248 free(matchability);
249
250 // Start determining the matched pairs
251 for (i = 0; i < nvtxs; i++) {
252 vtx_vec[i] = 0;
253 }
254 for (i = 0; i < nvtxs; i++) {
255 weighted_vtx_vec[i] = 0;
256 }
257
258 // relative weights of the different criteria
259 for (i = 0; i < nvtxs; i++) {
260 vtx = order[i];
261 if (mflag[vtx] >= 0) { /* already matched. */
262 continue;
263 }
264 inv_size = sqrt(1.0 / geom_graph[vtx].size);
265 sum_weights = fill_neighbors_vec(graph, vtx, weighted_vtx_vec);
266 fill_neighbors_vec_unweighted(graph, vtx, vtx_vec);
267 closest_neighbor = -1;
268
269 /*
270 We match node i with the "closest" neighbor, based on 4 criteria:
271 (1) (Weighted) fraction of common neighbors (measured on orig. graph)
272 (2) AvgDeg*AvgDeg/(deg(vtx)*deg(neighbor)) (degrees measured on orig. graph)
273 (3) AvgEdgeLen/dist(vtx,neighbor)
274 (4) Weight of normalized direct connection between nodes (measured on orig. graph)
275 */
276
277 for (j = 1; j < geom_graph[vtx].nedges; j++) {
278 neighbor = geom_graph[vtx].edges[j];
279 if (mflag[neighbor] >= 0) { /* already matched. */
280 continue;
281 }
282 // (1):
283 val =
284 A * unweighted_common_fraction(graph, vtx, neighbor,
285 vtx_vec);
286
287 if (val == 0 && (dist2_limit || !dist3(graph, vtx, neighbor))) {
288 // graph theoretical distance is larger than 3 (or 2 if '!dist3(graph, vtx, neighbor)' is commented)
289 // nodes cannot be matched
290 continue;
291 }
292 // (2)
293 val +=
294 B * avg_deg_2 / (graph[vtx].nedges *
295 graph[neighbor].nedges);
296
297
298 // (3)
299 val += C * avg_edge_len / ddist(geom_graph, vtx, neighbor);
300
301 // (4)
302 val +=
303 (weighted_vtx_vec[neighbor] * inv_size /
304 sqrt((float) geom_graph[neighbor].size)) /
305 max_norm_edge_weight;
306
307
308
309 if (val > closest_val || closest_neighbor == -1) {
310 closest_neighbor = neighbor;
311 closest_val = val;
312 }
313
314 }
315 if (closest_neighbor != -1) {
316 mflag[vtx] = closest_neighbor;
317 mflag[closest_neighbor] = vtx;
318 nmerged++;
319 }
320 empty_neighbors_vec(graph, vtx, vtx_vec);
321 empty_neighbors_vec(graph, vtx, weighted_vtx_vec);
322 }
323
324 free(order);
325 free(vtx_vec);
326 free(weighted_vtx_vec);
327 return (nmerged);
328 }
329
330 /* Construct mapping from original graph nodes to coarsened graph nodes */
makev2cv(int * mflag,int nvtxs,int * v2cv,int * cv2v)331 static void makev2cv(int *mflag, /* flag indicating vtx selected or not */
332 int nvtxs, /* number of vtxs in original graph */
333 int *v2cv, /* mapping from vtxs to coarsened vtxs */
334 int *cv2v /* mapping from coarsened vtxs to vtxs */
335 )
336 {
337 int i, j; /* loop counters */
338
339 j = 0;
340 for (i = 0; i < nvtxs; i++) {
341 if (mflag[i] < 0) { // unmatched node
342 v2cv[i] = j;
343 cv2v[2 * j] = i;
344 cv2v[2 * j + 1] = -1;
345 j++;
346 } else if (mflag[i] > i) { // matched node
347 v2cv[i] = j;
348 v2cv[mflag[i]] = j;
349 cv2v[2 * j] = i;
350 cv2v[2 * j + 1] = mflag[i];
351 j++;
352 }
353 }
354 }
355
make_coarse_graph(v_data * graph,int nvtxs,int nedges,v_data ** cgp,int cnvtxs,int * v2cv,int * cv2v)356 static int make_coarse_graph(v_data * graph, /* array of vtx data for graph */
357 int nvtxs, /* number of vertices in graph */
358 int nedges, /* number of edges in graph */
359 v_data ** cgp, /* coarsened version of graph */
360 int cnvtxs, /* number of vtxs in coarsened graph */
361 int *v2cv, /* mapping from vtxs to coarsened vtxs */
362 int *cv2v /* mapping from coarsened vtxs to vtxs */
363 )
364 // This function takes the information about matched pairs
365 // and use it to contract these pairs and build a coarse graph
366 {
367 int i, j, cv, v, neighbor, cv_nedges;
368 int cnedges = 0; /* number of edges in coarsened graph */
369 v_data *cgraph; /* coarsened version of graph */
370 int *index = N_NEW(cnvtxs, int);
371 float intra_weight;
372 /* An upper bound on the number of coarse graph edges. */
373 int maxCnedges = nedges; // do not subtract (nvtxs-cnvtxs) because we do not contract only along edges
374 int *edges;
375 float *eweights;
376 #ifdef STYLES
377 int styled_edges;
378 Style *styles = NULL;
379 #endif
380
381 for (i = 0; i < cnvtxs; i++) {
382 index[i] = 0;
383 }
384
385 /* Now allocate space for the new graph. Overeallocate and realloc later. */
386 cgraph = N_NEW(cnvtxs, v_data);
387 edges = N_NEW(2 * maxCnedges + cnvtxs, int);
388 eweights = N_NEW(2 * maxCnedges + cnvtxs, float);
389 #ifdef STYLES
390 styled_edges = (graph[0].styles != NULL);
391
392 if (styled_edges) {
393 styles = N_NEW(2 * maxCnedges + cnvtxs, Style);
394 }
395 #endif
396
397 if (graph[0].ewgts != NULL) {
398 // use edge weights
399 for (cv = 0; cv < cnvtxs; cv++) {
400
401 intra_weight = 0;
402
403 cgraph[cv].edges = edges;
404 cgraph[cv].ewgts = eweights;
405 #ifdef STYLES
406 cgraph[cv].styles = styles;
407 #endif
408
409 cv_nedges = 1;
410 v = cv2v[2 * cv];
411 for (j = 1; j < graph[v].nedges; j++) {
412 neighbor = v2cv[graph[v].edges[j]];
413 if (neighbor == cv) {
414 intra_weight = 2 * graph[v].ewgts[j]; // count both directions of the intra-edge
415 continue;
416 }
417 if (index[neighbor] == 0) { // new neighbor
418 index[neighbor] = cv_nedges;
419 cgraph[cv].edges[cv_nedges] = neighbor;
420 cgraph[cv].ewgts[cv_nedges] = graph[v].ewgts[j];
421 #ifdef STYLES
422 if (styled_edges) {
423 cgraph[cv].styles[cv_nedges] = graph[v].styles[j];
424 }
425 #endif
426 cv_nedges++;
427 } else {
428 cgraph[cv].ewgts[index[neighbor]] += graph[v].ewgts[j];
429 #ifdef STYLES
430 if (styled_edges
431 && graph[v].styles[j] !=
432 cgraph[cv].styles[index[neighbor]]) {
433 cgraph[cv].styles[index[neighbor]] = regular;
434 }
435 #endif
436 }
437 }
438
439 cgraph[cv].ewgts[0] = graph[v].ewgts[0];
440
441 if ((v = cv2v[2 * cv + 1]) != -1) {
442 for (j = 1; j < graph[v].nedges; j++) {
443 neighbor = v2cv[graph[v].edges[j]];
444 if (neighbor == cv)
445 continue;
446 if (index[neighbor] == 0) { // new neighbor
447 index[neighbor] = cv_nedges;
448 cgraph[cv].edges[cv_nedges] = neighbor;
449 cgraph[cv].ewgts[cv_nedges] = graph[v].ewgts[j];
450 #ifdef STYLES
451 if (styled_edges) {
452 cgraph[cv].styles[cv_nedges] =
453 graph[v].styles[j];
454 }
455 #endif
456 cv_nedges++;
457 } else {
458 cgraph[cv].ewgts[index[neighbor]] +=
459 graph[v].ewgts[j];
460 #ifdef STYLES
461 if (styled_edges
462 && graph[v].styles[j] !=
463 cgraph[cv].styles[index[neighbor]]) {
464 cgraph[cv].styles[index[neighbor]] = regular;
465 }
466 #endif
467 }
468 }
469 cgraph[cv].ewgts[0] += graph[v].ewgts[0] + intra_weight;
470 }
471 cgraph[cv].nedges = cv_nedges;
472 cgraph[cv].edges[0] = cv;
473 edges += cv_nedges;
474 eweights += cv_nedges;
475 cnedges += cv_nedges;
476 #ifdef STYLES
477 if (styled_edges) {
478 styles += cv_nedges;
479 }
480 #endif
481
482 for (j = 1; j < cgraph[cv].nedges; j++)
483 index[cgraph[cv].edges[j]] = 0;
484 }
485 } else { // fine graph is unweighted
486 int internal_weight = 0;
487
488 for (cv = 0; cv < cnvtxs; cv++) {
489
490 cgraph[cv].edges = edges;
491 cgraph[cv].ewgts = eweights;
492 #ifdef STYLES
493 cgraph[cv].styles = styles;
494 #endif
495
496 cv_nedges = 1;
497 v = cv2v[2 * cv];
498 for (j = 1; j < graph[v].nedges; j++) {
499 neighbor = v2cv[graph[v].edges[j]];
500 if (neighbor == cv) {
501 internal_weight = 2;
502 continue;
503 }
504 if (index[neighbor] == 0) { // new neighbor
505 index[neighbor] = cv_nedges;
506 cgraph[cv].edges[cv_nedges] = neighbor;
507 cgraph[cv].ewgts[cv_nedges] = -1;
508 #ifdef STYLES
509 if (styled_edges) {
510 cgraph[cv].styles[cv_nedges] = graph[v].styles[j];
511 }
512 #endif
513 cv_nedges++;
514 } else {
515 cgraph[cv].ewgts[index[neighbor]]--;
516 #ifdef STYLES
517 if (styled_edges
518 && graph[v].styles[j] !=
519 cgraph[cv].styles[index[neighbor]]) {
520 cgraph[cv].styles[index[neighbor]] = regular;
521 }
522 #endif
523 }
524 }
525 cgraph[cv].ewgts[0] = (float) graph[v].edges[0]; // this is our trick to store the weights on the diag in an unweighted graph
526 if ((v = cv2v[2 * cv + 1]) != -1) {
527 for (j = 1; j < graph[v].nedges; j++) {
528 neighbor = v2cv[graph[v].edges[j]];
529 if (neighbor == cv)
530 continue;
531 if (index[neighbor] == 0) { // new neighbor
532 index[neighbor] = cv_nedges;
533 cgraph[cv].edges[cv_nedges] = neighbor;
534 cgraph[cv].ewgts[cv_nedges] = -1;
535 #ifdef STYLES
536 if (styled_edges) {
537 cgraph[cv].styles[cv_nedges] =
538 graph[v].styles[j];
539 }
540 #endif
541 cv_nedges++;
542 } else {
543 cgraph[cv].ewgts[index[neighbor]]--;
544 #ifdef STYLES
545 if (styled_edges
546 && graph[v].styles[j] !=
547 cgraph[cv].styles[index[neighbor]]) {
548 cgraph[cv].styles[index[neighbor]] = regular;
549 }
550 #endif
551 }
552 }
553 // we subtract the weight of the intra-edge that was counted twice
554 cgraph[cv].ewgts[0] +=
555 (float) graph[v].edges[0] - internal_weight;
556 // In a case the edge weights are defined as positive:
557 //cgraph[cv].ewgts[0] += (float) graph[v].edges[0]+internal_weight;
558 }
559
560 cgraph[cv].nedges = cv_nedges;
561 cgraph[cv].edges[0] = cv;
562 edges += cv_nedges;
563 eweights += cv_nedges;
564 cnedges += cv_nedges;
565 #ifdef STYLES
566 if (styled_edges) {
567 styles += cv_nedges;
568 }
569 #endif
570
571 for (j = 1; j < cgraph[cv].nedges; j++)
572 index[cgraph[cv].edges[j]] = 0;
573 }
574 }
575 cnedges -= cnvtxs;
576 cnedges /= 2;
577 free(index);
578 *cgp = cgraph;
579 return cnedges;
580 }
581
582 static int
make_coarse_ex_graph(ex_vtx_data * graph,int nvtxs,int nedges,ex_vtx_data ** cgp,int cnvtxs,int * v2cv,int * cv2v)583 make_coarse_ex_graph (
584 ex_vtx_data * graph, /* array of vtx data for graph */
585 int nvtxs, /* number of vertices in graph */
586 int nedges, /* number of edges in graph */
587 ex_vtx_data ** cgp, /* coarsened version of graph */
588 int cnvtxs, /* number of vtxs in coarsened graph */
589 int *v2cv, /* mapping from vtxs to coarsened vtxs */
590 int *cv2v /* mapping from coarsened vtxs to vtxs */
591 )
592 // This function takes the information about matched pairs
593 // and use it to contract these pairs and build a coarse ex_graph
594 {
595 int cnedges; /* number of edges in coarsened graph */
596 ex_vtx_data *cgraph; /* coarsened version of graph */
597 int i, j, cv, v, neighbor, cv_nedges;
598 int *index = N_NEW(cnvtxs, int);
599 int *edges;
600
601 for (i = 0; i < cnvtxs; i++) {
602 index[i] = 0;
603 }
604
605 /* An upper bound on the number of coarse graph edges. */
606 cnedges = nedges;
607
608 /* Now allocate space for the new graph. Overeallocate and realloc later. */
609 cgraph = N_NEW(cnvtxs, ex_vtx_data);
610 edges = N_NEW(2 * cnedges + cnvtxs, int);
611
612 for (cv = 0; cv < cnvtxs; cv++) {
613
614 cgraph[cv].edges = edges;
615
616 cv_nedges = 1;
617 v = cv2v[2 * cv];
618 for (j = 1; j < graph[v].nedges; j++) {
619 neighbor = v2cv[graph[v].edges[j]];
620 if (neighbor == cv) {
621 continue;
622 }
623 if (index[neighbor] == 0) { // new neighbor
624 index[neighbor] = cv_nedges;
625 cgraph[cv].edges[cv_nedges] = neighbor;
626 cv_nedges++;
627 }
628 }
629 cgraph[cv].size = graph[v].size;
630 cgraph[cv].x_coord = graph[v].x_coord;
631 cgraph[cv].y_coord = graph[v].y_coord;
632 if ((v = cv2v[2 * cv + 1]) != -1) {
633 for (j = 1; j < graph[v].nedges; j++) {
634 neighbor = v2cv[graph[v].edges[j]];
635 if (neighbor == cv)
636 continue;
637 if (index[neighbor] == 0) { // new neighbor
638 index[neighbor] = cv_nedges;
639 cgraph[cv].edges[cv_nedges] = neighbor;
640 cv_nedges++;
641 }
642 }
643 // compute new coord's as a weighted average of the old ones
644 cgraph[cv].x_coord =
645 (cgraph[cv].size * cgraph[cv].x_coord +
646 graph[v].size * graph[v].x_coord) / (cgraph[cv].size +
647 graph[v].size);
648 cgraph[cv].y_coord =
649 (cgraph[cv].size * cgraph[cv].y_coord +
650 graph[v].size * graph[v].y_coord) / (cgraph[cv].size +
651 graph[v].size);
652 cgraph[cv].size += graph[v].size;
653 }
654 cgraph[cv].nedges = cv_nedges;
655 cgraph[cv].edges[0] = cv;
656 edges += cv_nedges;
657
658 for (j = 1; j < cgraph[cv].nedges; j++)
659 index[cgraph[cv].edges[j]] = 0;
660 }
661 free(index);
662 *cgp = cgraph;
663 return cnedges;
664 }
665
666 static void
coarsen_match(v_data * graph,ex_vtx_data * geom_graph,int nvtxs,int nedges,int geom_nedges,v_data ** cgraph,ex_vtx_data ** cgeom_graph,int * cnp,int * cnedges,int * cgeom_nedges,int ** v2cvp,int ** cv2vp,int dist2_limit)667 coarsen_match (
668 v_data * graph, /* graph to be matched */
669 ex_vtx_data* geom_graph, /* another graph (with coords) on the same nodes */
670 int nvtxs, /* number of vertices in graph */
671 int nedges, /* number of edges in graph */
672 int geom_nedges, /* number of edges in geom_graph */
673 v_data ** cgraph, /* coarsened version of graph */
674 ex_vtx_data ** cgeom_graph, /* coarsened version of geom_graph */
675 int *cnp, /* number of vtxs in coarsened graph */
676 int *cnedges, /* number of edges in coarsened graph */
677 int *cgeom_nedges, /* number of edges in coarsened geom_graph */
678 int **v2cvp, /* reference from vertices to coarse vertices */
679 int **cv2vp, /* reference from vertices to coarse vertices */
680 int dist2_limit
681 )
682
683 /*
684 * This function gets two graphs with the same node set and
685 * constructs two corresponding coarsened graphs of about
686 * half the size
687 */
688 {
689 int *mflag; /* flag indicating vtx matched or not */
690 int nmerged; /* number of edges contracted */
691 int *v2cv; /* reference from vertices to coarse vertices */
692 int *cv2v; /* reference from vertices to coarse vertices */
693 int cnvtxs;
694
695 /* Allocate and initialize space. */
696 mflag = N_NEW(nvtxs, int);
697
698 /* Find a maximal matching in the graphs */
699 nmerged = maxmatch(graph, geom_graph, nvtxs, mflag, dist2_limit);
700
701 /* Now construct coarser graph by contracting along matching edges. */
702 /* Pairs of values in mflag array indicate matched vertices. */
703 /* A negative value indicates that vertex is unmatched. */
704
705 *cnp = cnvtxs = nvtxs - nmerged;
706
707 *v2cvp = v2cv = N_NEW(nvtxs, int);
708 *cv2vp = cv2v = N_NEW(2 * cnvtxs, int);
709 makev2cv(mflag, nvtxs, v2cv, cv2v);
710
711 free(mflag);
712
713 *cnedges =
714 make_coarse_graph(graph, nvtxs, nedges, cgraph, cnvtxs, v2cv,
715 cv2v);
716 *cgeom_nedges =
717 make_coarse_ex_graph(geom_graph, nvtxs, geom_nedges, cgeom_graph,
718 cnvtxs, v2cv, cv2v);
719 }
720
721 /* release:
722 * Free memory resources for hierarchy.
723 */
release(Hierarchy * hierarchy)724 void release(Hierarchy * hierarchy)
725 {
726 v_data *graph;
727 ex_vtx_data *ex_graph;
728 int i;
729 for (i = 0; i < hierarchy->nlevels; i++) {
730 graph = hierarchy->graphs[i];
731 ex_graph = hierarchy->geom_graphs[i];
732 freeGraph (graph);
733 free(ex_graph[0].edges);
734 free(ex_graph);
735 if (i < hierarchy->nlevels - 1) {
736 free(hierarchy->v2cv[i]);
737 }
738 if (i > 0) {
739 free(hierarchy->cv2v[i]);
740 }
741 }
742
743 free(hierarchy->graphs);
744 free(hierarchy->geom_graphs);
745 free(hierarchy->nvtxs);
746 free(hierarchy->nedges);
747 free(hierarchy->cv2v);
748 free(hierarchy->v2cv);
749 }
750
cpGraph(v_data * graph,int n,int nedges)751 static v_data *cpGraph(v_data * graph, int n, int nedges)
752 {
753 v_data *cpGraph;
754 int *edges;
755 float *ewgts = NULL;
756 #ifdef STYLES
757 Style *styles = NULL;
758 #endif
759 int i, j;
760
761 if (graph == NULL || n == 0) {
762 return NULL;
763 }
764 cpGraph = N_NEW(n, v_data);
765 edges = N_NEW(2 * nedges + n, int);
766 if (graph[0].ewgts != NULL) {
767 ewgts = N_NEW(2 * nedges + n, float);
768 }
769 #ifdef STYLES
770 if (graph[0].styles != NULL) {
771 styles = N_NEW(2 * nedges + n, Style);
772 }
773 #endif
774
775 for (i = 0; i < n; i++) {
776 cpGraph[i] = graph[i];
777 cpGraph[i].edges = edges;
778 cpGraph[i].ewgts = ewgts;
779 #ifdef STYLES
780 cpGraph[i].styles = styles;
781 #endif
782 for (j = 0; j < graph[i].nedges; j++) {
783 edges[j] = graph[i].edges[j];
784 }
785 edges += graph[i].nedges;
786 if (ewgts != NULL) {
787 for (j = 0; j < graph[i].nedges; j++) {
788 ewgts[j] = graph[i].ewgts[j];
789 }
790 ewgts += graph[i].nedges;
791 }
792 #ifdef STYLES
793 if (styles != NULL) {
794 for (j = 0; j < graph[i].nedges; j++) {
795 styles[j] = graph[i].styles[j];
796 }
797 styles += graph[i].nedges;
798 }
799 #endif
800 }
801 return cpGraph;
802 }
803
cpExGraph(ex_vtx_data * graph,int n,int nedges)804 static ex_vtx_data *cpExGraph(ex_vtx_data * graph, int n, int nedges)
805 {
806 ex_vtx_data *cpGraph;
807 int *edges;
808 int i, j;
809
810 if (graph == NULL || n == 0) {
811 return NULL;
812 }
813 cpGraph = N_NEW(n, ex_vtx_data);
814 edges = N_NEW(2 * nedges + n, int);
815
816 for (i = 0; i < n; i++) {
817 cpGraph[i] = graph[i];
818 cpGraph[i].edges = edges;
819 for (j = 0; j < graph[i].nedges; j++) {
820 edges[j] = graph[i].edges[j];
821 }
822 edges += graph[i].nedges;
823 }
824 return cpGraph;
825 }
826
create_hierarchy(v_data * graph,int nvtxs,int nedges,ex_vtx_data * geom_graph,int ngeom_edges,hierparms_t * parms)827 Hierarchy *create_hierarchy(v_data * graph, int nvtxs, int nedges,
828 ex_vtx_data * geom_graph, int ngeom_edges,
829 hierparms_t* parms)
830 {
831 int cur_level;
832 Hierarchy *hierarchy = NEW(Hierarchy);
833 int cngeom_edges = ngeom_edges;
834 ex_vtx_data *geom_graph_level;
835 int nodeIndex = 0;
836 int i, j;
837 int min_nvtxs = parms->min_nvtxs;
838 int nlevels = MAX(5, 10 * (int) log((float) (nvtxs / min_nvtxs))); // just an estimate
839
840 hierarchy->graphs = N_NEW(nlevels, v_data *);
841 hierarchy->geom_graphs = N_NEW(nlevels, ex_vtx_data *);
842 hierarchy->nvtxs = N_NEW(nlevels, int);
843 hierarchy->nedges = N_NEW(nlevels, int);
844 hierarchy->v2cv = N_NEW(nlevels, int *);
845 hierarchy->cv2v = N_NEW(nlevels, int *);
846
847 hierarchy->graphs[0] = cpGraph(graph, nvtxs, nedges);
848 hierarchy->geom_graphs[0] = cpExGraph(geom_graph, nvtxs, ngeom_edges);
849 hierarchy->nvtxs[0] = nvtxs;
850 hierarchy->nedges[0] = nedges;
851
852 for (cur_level = 0;
853 hierarchy->nvtxs[cur_level] > min_nvtxs
854 && cur_level < 50 /*nvtxs/10 */ ; cur_level++) {
855 if (cur_level == nlevels - 1) { // we have to allocate more space
856 nlevels *= 2;
857 hierarchy->graphs =
858 RALLOC(nlevels, hierarchy->graphs, v_data *);
859 hierarchy->geom_graphs =
860 RALLOC(nlevels, hierarchy->geom_graphs, ex_vtx_data *);
861 hierarchy->nvtxs = RALLOC(nlevels, hierarchy->nvtxs, int);
862 hierarchy->nedges = RALLOC(nlevels, hierarchy->nedges, int);
863 hierarchy->v2cv = RALLOC(nlevels, hierarchy->v2cv, int *);
864 hierarchy->cv2v = RALLOC(nlevels, hierarchy->cv2v, int *);
865 }
866
867 ngeom_edges = cngeom_edges;
868 coarsen_match
869 (hierarchy->graphs[cur_level],
870 hierarchy->geom_graphs[cur_level],
871 hierarchy->nvtxs[cur_level], hierarchy->nedges[cur_level],
872 ngeom_edges, &hierarchy->graphs[cur_level + 1],
873 &hierarchy->geom_graphs[cur_level + 1],
874 &hierarchy->nvtxs[cur_level + 1],
875 &hierarchy->nedges[cur_level + 1], &cngeom_edges,
876 &hierarchy->v2cv[cur_level], &hierarchy->cv2v[cur_level + 1],
877 parms->dist2_limit);
878 }
879
880 hierarchy->nlevels = cur_level + 1;
881
882 // assign consecutive global identifiers to all nodes on hierarchy
883 for (i = 0; i < hierarchy->nlevels; i++) {
884 geom_graph_level = hierarchy->geom_graphs[i];
885 for (j = 0; j < hierarchy->nvtxs[i]; j++) {
886 geom_graph_level[j].globalIndex = nodeIndex;
887 nodeIndex++;
888 }
889 }
890 hierarchy->maxNodeIndex = nodeIndex;
891 return hierarchy;
892 }
893
894 static double
dist_from_foci(ex_vtx_data * graph,int node,int * foci,int num_foci)895 dist_from_foci(ex_vtx_data * graph, int node, int *foci, int num_foci)
896 {
897 // compute minimum distance of 'node' from the set 'foci'
898 int i;
899 double distance = ddist(graph, node, foci[0]);
900 for (i = 1; i < num_foci; i++) {
901 distance = MIN(distance, ddist(graph, node, foci[i]));
902 }
903
904 return distance;
905 }
906
907 /* set_active_levels:
908 * Compute the "active level" field of each node in the hierarchy.
909 * Note that if the active level is lower than the node's level, the node
910 * is "split" in the presentation; if the active level is higher than
911 * the node's level, then the node is aggregated into a coarser node.
912 * If the active level equals the node's level then the node is currently shown
913 */
914 void
set_active_levels(Hierarchy * hierarchy,int * foci_nodes,int num_foci,levelparms_t * parms)915 set_active_levels(Hierarchy * hierarchy, int *foci_nodes, int num_foci,
916 levelparms_t* parms)
917 {
918 int n, i;
919 int *nodes;
920 double *distances;
921 ex_vtx_data *graph;
922 int level;
923 int group_size;
924 int thresh;
925 int vtx;
926 ex_vtx_data *cgraph;
927 int *cv2v;
928 int v, u;
929 int min_level = cur_level;
930
931 graph = hierarchy->geom_graphs[min_level]; // finest graph
932 n = hierarchy->nvtxs[min_level];
933
934 // compute distances from foci nodes
935 nodes = N_NEW(n, int);
936 distances = N_NEW(n, double);
937 for (i = 0; i < n; i++) {
938 nodes[i] = i;
939 distances[i] = dist_from_foci(graph, i, foci_nodes, num_foci);
940 }
941
942 // sort nodes according to their distance from foci
943 quicksort_place(distances, nodes, 0, n - 1);
944
945 /* compute *desired* levels of fine nodes by distributing them into buckets
946 * The sizes of the buckets is a geometric series with
947 * factor: 'coarsening_rate'
948 */
949 level = min_level;
950 group_size = parms->num_fine_nodes * num_foci;
951 thresh = group_size;
952 for (i = 0; i < n; i++) {
953 vtx = nodes[i];
954 if (i > thresh && level < hierarchy->nlevels - 1) {
955 level++;
956 group_size = (int) (group_size * parms->coarsening_rate);
957 thresh += group_size;
958 }
959 graph[vtx].active_level = level;
960 }
961
962 // Fine-to-coarse sweep:
963 //----------------------
964 // Propagate levels to all coarse nodes and determine final levels
965 // at lowest meeting points. Note that nodes can be active in
966 // lower (finer) levels than what originally desired, since if 'u'
967 // and 'v' are merged, than the active level of '{u,v}' will be
968 // the minimum of the active levels of 'u' and 'v'
969 for (level = min_level + 1; level < hierarchy->nlevels; level++) {
970 cgraph = hierarchy->geom_graphs[level];
971 graph = hierarchy->geom_graphs[level - 1];
972 cv2v = hierarchy->cv2v[level];
973 n = hierarchy->nvtxs[level];
974 for (i = 0; i < n; i++) {
975 v = cv2v[2 * i];
976 u = cv2v[2 * i + 1];
977 if (u >= 0) { // cv is decomposed from 2 fine nodes
978 if (graph[v].active_level < level
979 || graph[u].active_level < level) {
980 // At least one of the nodes should be active at a lower level,
981 // in this case both children are active at a lower level
982 // and we don't wait till they are merged
983 graph[v].active_level =
984 MIN(graph[v].active_level, level - 1);
985 graph[u].active_level =
986 MIN(graph[u].active_level, level - 1);
987 }
988 // The node with the finer (lower) active level determines the coarse active level
989 cgraph[i].active_level =
990 MIN(graph[v].active_level, graph[u].active_level);
991 } else {
992 cgraph[i].active_level = graph[v].active_level;
993 }
994 }
995 }
996
997 // Coarse-to-fine sweep:
998 //----------------------
999 // Propagate final levels all the way to fine nodes
1000 for (level = hierarchy->nlevels - 1; level > 0; level--) {
1001 cgraph = hierarchy->geom_graphs[level];
1002 graph = hierarchy->geom_graphs[level - 1];
1003 cv2v = hierarchy->cv2v[level];
1004 n = hierarchy->nvtxs[level];
1005 for (i = 0; i < n; i++) {
1006 if (cgraph[i].active_level < level) {
1007 continue;
1008 }
1009 // active level has been already reached, copy level to children
1010 v = cv2v[2 * i];
1011 u = cv2v[2 * i + 1];
1012 graph[v].active_level = cgraph[i].active_level;
1013 if (u >= 0) {
1014 graph[u].active_level = cgraph[i].active_level;
1015 }
1016 }
1017 }
1018 free(nodes);
1019 free(distances);
1020 }
1021
1022 /* findClosestActiveNode:
1023 * Given (x,y) in physical coords, check if node is closer to this point
1024 * than previous setting. If so, reset values.
1025 * If node is not active, recurse down finer levels.
1026 * Return closest distance squared.
1027 */
1028 static double
findClosestActiveNode(Hierarchy * hierarchy,int node,int level,double x,double y,double closest_dist,int * closest_node,int * closest_node_level)1029 findClosestActiveNode(Hierarchy * hierarchy, int node,
1030 int level, double x, double y,
1031 double closest_dist, int *closest_node,
1032 int *closest_node_level)
1033 {
1034 ex_vtx_data *graph;
1035
1036 graph = hierarchy->geom_graphs[level];
1037
1038 if (graph[node].active_level == level)
1039 { // node is active
1040 double delx = x - graph[node].physical_x_coord;
1041 double dely = y - graph[node].physical_y_coord;
1042 double dist = delx*delx + dely*dely;
1043
1044 if (dist < closest_dist)
1045 {
1046 closest_dist = dist;
1047 *closest_node = node;
1048 *closest_node_level = level;
1049
1050
1051 }
1052 return closest_dist;
1053 }
1054
1055 closest_dist =
1056 findClosestActiveNode(hierarchy, hierarchy->cv2v[level][2 * node],
1057 level - 1, x, y, closest_dist, closest_node,
1058 closest_node_level);
1059
1060 if (hierarchy->cv2v[level][2 * node + 1] >= 0) {
1061 closest_dist =
1062 findClosestActiveNode(hierarchy,
1063 hierarchy->cv2v[level][2 * node + 1],
1064 level - 1, x, y, closest_dist,
1065 closest_node, closest_node_level);
1066 }
1067 return closest_dist;
1068 }
1069
1070 /* find_leftmost_descendant:
1071 * Given coarse node in given level, return representative node
1072 * in lower level cur_level.
1073 */
1074 static int
find_leftmost_descendant(Hierarchy * hierarchy,int node,int level,int cur_level)1075 find_leftmost_descendant(Hierarchy * hierarchy, int node, int level,
1076 int cur_level)
1077 {
1078 while (level > cur_level)
1079 {
1080 node = hierarchy->cv2v[level--][2 * node];
1081 }
1082 return node;
1083 }
1084
1085 /* find_closest_active_node:
1086 * Given x and y in physical coordinate system, determine closest
1087 * actual node in graph. Store this in closest_fine_node, and return
1088 * distance squared.
1089 */
1090 double
find_closest_active_node(Hierarchy * hierarchy,double x,double y,int * closest_fine_node)1091 find_closest_active_node(Hierarchy * hierarchy, double x, double y,
1092 int *closest_fine_node)
1093 {
1094 int i, closest_node, closest_node_level;
1095 int top_level = hierarchy->nlevels - 1;
1096 double min_dist = 1e20;
1097
1098 for (i = 0; i < hierarchy->nvtxs[top_level]; i++)
1099 {
1100 min_dist = findClosestActiveNode(hierarchy, i, top_level, x, y,min_dist, &closest_node, &closest_node_level);
1101 }
1102 *closest_fine_node =find_leftmost_descendant(hierarchy, closest_node,closest_node_level, cur_level);
1103
1104 return min_dist;
1105 }
1106
1107 #if 0
1108 int find_random_descendant(Hierarchy * hierarchy, int node, int level,
1109 int cur_level)
1110 {
1111 int inc;
1112 while (level > cur_level) {
1113 if (hierarchy->cv2v[level][2 * node + 1] >= 0) {
1114 inc = rand() % 2;
1115 } else {
1116 inc = 0;
1117 }
1118 node = hierarchy->cv2v[level--][2 * node + inc];
1119 }
1120 return node;
1121 }
1122 #endif
1123
1124 int
init_ex_graph(v_data * graph1,v_data * graph2,int n,double * x_coords,double * y_coords,ex_vtx_data ** gp)1125 init_ex_graph(v_data * graph1, v_data * graph2, int n,
1126 double *x_coords, double *y_coords, ex_vtx_data ** gp)
1127 {
1128 // build ex_graph from the union of edges in 'graph1' and 'graph2'
1129 // note that this function does not destroy the input graphs
1130
1131 ex_vtx_data *geom_graph;
1132 int nedges1 = 0, nedges2 = 0;
1133 int *edges;
1134 int nedges = 0;
1135 int i, j, k, l, first_nedges;
1136 int neighbor;
1137 for (i = 0; i < n; i++) {
1138 nedges1 += graph1[i].nedges;
1139 nedges2 += graph2[i].nedges;
1140 }
1141 edges = N_NEW(nedges1 + nedges2, int);
1142 *gp = geom_graph = N_NEW(n, ex_vtx_data);
1143
1144 for (i = 0; i < n; i++) {
1145 geom_graph[i].edges = edges;
1146 geom_graph[i].size = 1;
1147 geom_graph[i].x_coord = (float) x_coords[i];
1148 geom_graph[i].y_coord = (float) y_coords[i];
1149 geom_graph[i].edges[0] = i;
1150 for (j = 1; j < graph1[i].nedges; j++) {
1151 edges[j] = graph1[i].edges[j];
1152 }
1153 first_nedges = k = graph1[i].nedges;
1154 for (j = 1; j < graph2[i].nedges; j++) {
1155 neighbor = graph2[i].edges[j];
1156 for (l = 1; l < first_nedges; l++) {
1157 if (edges[l] == neighbor) { // already existed neighbor
1158 break;
1159 }
1160 }
1161 if (l == first_nedges) { // neighbor wasn't found
1162 edges[k++] = neighbor;
1163 }
1164 }
1165 geom_graph[i].nedges = k;
1166 edges += k;
1167 nedges += k;
1168 }
1169 nedges /= 2;
1170 return nedges;
1171 }
1172
1173 /* extract_active_logical_coords:
1174 * Preorder scan the hierarchy tree, and extract the logical coordinates of
1175 * all active nodes
1176 * Store (universal) coords in x_coords and y_coords and increment index.
1177 * Return index.
1178 */
1179 int
extract_active_logical_coords(Hierarchy * hierarchy,int node,int level,double * x_coords,double * y_coords,int counter)1180 extract_active_logical_coords(Hierarchy * hierarchy, int node,
1181 int level, double *x_coords,
1182 double *y_coords, int counter)
1183 {
1184
1185 ex_vtx_data *graph = hierarchy->geom_graphs[level];
1186
1187 if (graph[node].active_level == level) { // node is active
1188 x_coords[counter] = graph[node].x_coord;
1189 y_coords[counter++] = graph[node].y_coord;
1190 return counter;
1191 }
1192
1193 counter =
1194 extract_active_logical_coords(hierarchy,
1195 hierarchy->cv2v[level][2 * node],
1196 level - 1, x_coords, y_coords,
1197 counter);
1198
1199 if (hierarchy->cv2v[level][2 * node + 1] >= 0) {
1200 counter =
1201 extract_active_logical_coords(hierarchy,
1202 hierarchy->cv2v[level][2 * node +
1203 1],
1204 level - 1, x_coords, y_coords,
1205 counter);
1206 }
1207 return counter;
1208 }
1209
1210 /* set_active_physical_coords:
1211 * Preorder scan the hierarchy tree, and set the physical coordinates
1212 * of all active nodes
1213 */
1214 int
set_active_physical_coords(Hierarchy * hierarchy,int node,int level,double * x_coords,double * y_coords,int counter)1215 set_active_physical_coords(Hierarchy * hierarchy, int node, int level,
1216 double *x_coords, double *y_coords, int counter)
1217 {
1218
1219 ex_vtx_data *graph = hierarchy->geom_graphs[level];
1220
1221 if (graph[node].active_level == level) { // node is active
1222 graph[node].physical_x_coord = (float) x_coords[counter];
1223 graph[node].physical_y_coord = (float) y_coords[counter++];
1224 return counter;
1225 }
1226
1227 counter =
1228 set_active_physical_coords(hierarchy,
1229 hierarchy->cv2v[level][2*node],
1230 level - 1, x_coords, y_coords, counter);
1231
1232 if (hierarchy->cv2v[level][2 * node + 1] >= 0) {
1233 counter =
1234 set_active_physical_coords(hierarchy,
1235 hierarchy->cv2v[level][2*node + 1],
1236 level - 1, x_coords, y_coords,
1237 counter);
1238 }
1239 return counter;
1240 }
1241
countActiveNodes(Hierarchy * hierarchy,int node,int level)1242 static int countActiveNodes(Hierarchy * hierarchy, int node, int level)
1243 {
1244 ex_vtx_data *graph = hierarchy->geom_graphs[level];
1245 int cnt, other;
1246
1247 if (graph[node].active_level == level) { // node is active
1248 #ifdef DEBUG
1249 fprintf (stderr, "(%d,%d) (%f,%f)\n", level,node,graph[node].x_coord,graph[node].y_coord);
1250 #endif
1251 return 1;
1252 }
1253 cnt = countActiveNodes(hierarchy, hierarchy->cv2v[level][2*node], level-1);
1254
1255 if ((other = hierarchy->cv2v[level][2 * node + 1]) >= 0) {
1256 cnt += countActiveNodes(hierarchy, other, level - 1);
1257 }
1258 return cnt;
1259 }
1260
1261 /* count_active_nodes:
1262 * Return number of active nodes.
1263 */
count_active_nodes(Hierarchy * hierarchy)1264 int count_active_nodes(Hierarchy * hierarchy)
1265 {
1266 int i = 0;
1267 int max_level = hierarchy->nlevels - 1; // coarsest level
1268 int sum = 0;
1269 for (i = 0; i < hierarchy->nvtxs[max_level]; i++) {
1270 sum += countActiveNodes(hierarchy, i, max_level);
1271 }
1272 return sum;
1273 }
1274
1275 /* locateByIndex:
1276 * Given global index, find level and index on level.
1277 * Return -1 if no such node.
1278 */
locateByIndex(Hierarchy * hierarchy,int index,int * lp)1279 int locateByIndex(Hierarchy * hierarchy, int index, int *lp)
1280 {
1281 int globalIndex;
1282 int level;
1283 int nlevels;
1284
1285 assert(hierarchy);
1286 globalIndex = index;
1287 nlevels = hierarchy->nlevels;
1288 for (level = 0; level < nlevels && index >= hierarchy->nvtxs[level];
1289 level++) {
1290 index -= hierarchy->nvtxs[level];
1291 }
1292 if (level < nlevels && index >= 0
1293 && hierarchy->geom_graphs[level][index].globalIndex ==
1294 globalIndex) {
1295 *lp = level;
1296 return index;
1297 } else {
1298 // index not found
1299 // return an arbitrary node
1300 *lp = 0;
1301 return -1;
1302 }
1303 }
1304
1305 /* isActiveAncestorOfNeighbors:
1306 * check whether 'activeAncestorIdx' is an active ancestor of one
1307 * of the neighbors of 'node'
1308 */
1309 static int
isActiveAncestorOfNeighbors(Hierarchy * hierarchy,int node,int level,int activeAncestorIdx)1310 isActiveAncestorOfNeighbors(Hierarchy * hierarchy, int node, int level,
1311 int activeAncestorIdx)
1312 {
1313 int i, active_level ;
1314 v_data neighborsInLevel;
1315 int neighbor, neighborLevel;
1316 assert(hierarchy);
1317 neighborsInLevel = hierarchy->graphs[level][node];
1318
1319 for (i = 1; i < neighborsInLevel.nedges; i++) {
1320 neighbor = neighborsInLevel.edges[i];
1321 active_level =
1322 hierarchy->geom_graphs[level][neighbor].active_level;
1323 if (active_level > level) {
1324 // ancestor of neighbor is active
1325 neighborLevel = level;
1326 do {
1327 neighbor = hierarchy->v2cv[neighborLevel][neighbor];
1328 neighborLevel++;
1329 } while (active_level > neighborLevel);
1330 if (hierarchy->geom_graphs[neighborLevel][neighbor].
1331 globalIndex == activeAncestorIdx) {
1332 return 1;
1333 }
1334 }
1335 }
1336 return 0;
1337 }
1338
1339 /* findGlobalIndexesOfActiveNeighbors:
1340 * Find indices of active neighbors. Store in allocated array.
1341 * Return pointer to array in np, and return number of neighbors.
1342 * Return -1 on error
1343 */
1344 int
findGlobalIndexesOfActiveNeighbors(Hierarchy * hierarchy,int index,int ** np)1345 findGlobalIndexesOfActiveNeighbors(Hierarchy * hierarchy, int index,
1346 int **np)
1347 {
1348 int numNeighbors = 0;
1349 int *neighbors;
1350 int i, j;
1351 int level, node,active_level,found;
1352 v_data neighborsInLevel;
1353 int nAllocNeighbors;
1354 int *stack; // 4*hierarchy->nlevels should be enough for the DFS scan
1355 int stackHeight;
1356 int neighbor, neighborLevel;
1357
1358 if (hierarchy == NULL) {
1359 return -1;
1360 }
1361
1362 if ((node = locateByIndex(hierarchy, index, &level)) < 0)
1363 node = 0;
1364
1365 neighborsInLevel = hierarchy->graphs[level][node];
1366 nAllocNeighbors = 2 * neighborsInLevel.nedges;
1367 neighbors = N_NEW(nAllocNeighbors, int);
1368
1369 stack = N_NEW(5 * hierarchy->nlevels + 1, int);
1370
1371 for (i = 1; i < neighborsInLevel.nedges; i++) {
1372 neighbor = neighborsInLevel.edges[i];
1373 active_level =
1374 hierarchy->geom_graphs[level][neighbor].active_level;
1375 if (active_level == level) {
1376 // neighbor is active - add it
1377 if (numNeighbors >= nAllocNeighbors) {
1378 nAllocNeighbors = 2 * nAllocNeighbors + 1;
1379 neighbors = RALLOC(nAllocNeighbors, neighbors, int);
1380 }
1381 neighbors[numNeighbors] =
1382 hierarchy->geom_graphs[level][neighbor].globalIndex;
1383 numNeighbors++;
1384 } else if (active_level > level) {
1385 // ancestor of neighbor is active - add it if not already added
1386 neighborLevel = level;
1387 do {
1388
1389 neighbor = hierarchy->v2cv[neighborLevel][neighbor];
1390 neighborLevel++;
1391 } while (active_level > neighborLevel);
1392 found = 0;
1393 for (j = 0; j < numNeighbors && !found; j++) {
1394 if (neighbors[j] ==
1395 hierarchy->geom_graphs[neighborLevel][neighbor].
1396 globalIndex) {
1397 found = 1;
1398 }
1399 }
1400 if (!found) {
1401 if (numNeighbors >= nAllocNeighbors) {
1402 nAllocNeighbors = 2 * nAllocNeighbors + 1;
1403 neighbors = RALLOC(nAllocNeighbors, neighbors, int);
1404 }
1405 neighbors[numNeighbors] =
1406 hierarchy->geom_graphs[neighborLevel][neighbor].
1407 globalIndex;
1408 numNeighbors++;
1409 }
1410 } else {
1411 // descendants of neighbor are active - add those of them that really point back
1412 // using A DFS search below neighbor
1413 stack[0] = level;
1414 stack[1] = neighbor;
1415 stackHeight = 2;
1416 while (stackHeight > 0) {
1417 stackHeight--;
1418 neighbor = stack[stackHeight];
1419 stackHeight--;
1420 neighborLevel = stack[stackHeight];
1421 if (hierarchy->geom_graphs[neighborLevel][neighbor].
1422 active_level == neighborLevel) {
1423 if (numNeighbors >= nAllocNeighbors) {
1424 nAllocNeighbors = 2 * nAllocNeighbors + 1;
1425 neighbors =
1426 RALLOC(nAllocNeighbors, neighbors, int);
1427 }
1428 neighbors[numNeighbors] =
1429 hierarchy->geom_graphs[neighborLevel][neighbor].
1430 globalIndex;
1431 numNeighbors++;
1432 } else if (hierarchy->geom_graphs[neighborLevel][neighbor].
1433 active_level < level) {
1434 // check if node points back to original node (or just was clustered with neighbors)
1435
1436 if (isActiveAncestorOfNeighbors
1437 (hierarchy,
1438 hierarchy->cv2v[neighborLevel][2 * neighbor],
1439 neighborLevel - 1, index)) {
1440 stack[stackHeight] = neighborLevel - 1;
1441 stackHeight++;
1442 stack[stackHeight] =
1443 hierarchy->cv2v[neighborLevel][2 * neighbor];
1444 stackHeight++;
1445 }
1446 if (hierarchy->cv2v[neighborLevel][2 * neighbor + 1] >=
1447 0) {
1448
1449 if (isActiveAncestorOfNeighbors
1450 (hierarchy,
1451 hierarchy->cv2v[neighborLevel][2 * neighbor +
1452 1],
1453 neighborLevel - 1, index)) {
1454 stack[stackHeight] = neighborLevel - 1;
1455 stackHeight++;
1456 stack[stackHeight] =
1457 hierarchy->cv2v[neighborLevel][2 *
1458 neighbor +
1459 1];
1460 stackHeight++;
1461 }
1462 }
1463 }
1464 }
1465 }
1466 }
1467 free(stack);
1468 *np = neighbors;
1469 return numNeighbors;
1470 }
1471
1472 /* find_physical_coords:
1473 * find the 'physical_coords' of the active-ancestor of 'node'
1474 */
1475 void
find_physical_coords(Hierarchy * hierarchy,int level,int node,double * x,double * y)1476 find_physical_coords(Hierarchy * hierarchy, int level, int node, double *x,
1477 double *y)
1478 {
1479 int active_level = hierarchy->geom_graphs[level][node].active_level;
1480 while (active_level > level) {
1481 node = hierarchy->v2cv[level][node];
1482 level++;
1483 }
1484
1485 *x = hierarchy->geom_graphs[level][node].physical_x_coord;
1486 *y = hierarchy->geom_graphs[level][node].physical_y_coord;
1487 }
1488
1489 void
find_active_ancestor_info(Hierarchy * hierarchy,int level,int node,int * levell,int * nodee)1490 find_active_ancestor_info(Hierarchy * hierarchy, int level, int node, int *levell,int *nodee)
1491 {
1492 int active_level = hierarchy->geom_graphs[level][node].active_level;
1493 while (active_level > level) {
1494 node = hierarchy->v2cv[level][node];
1495 level++;
1496 }
1497
1498 *nodee = node;
1499 *levell = level;
1500 }
1501
1502
1503
1504
1505 /* find_old_physical_coords:
1506 * find the 'old_physical_coords' of the old active-ancestor of 'node'
1507 */
1508 void
find_old_physical_coords(Hierarchy * hierarchy,int level,int node,double * x,double * y)1509 find_old_physical_coords(Hierarchy * hierarchy, int level, int node, double *x,
1510 double *y)
1511 {
1512 int active_level = hierarchy->geom_graphs[level][node].old_active_level;
1513 while (active_level > level) {
1514 node = hierarchy->v2cv[level][node];
1515 level++;
1516 }
1517
1518 *x = hierarchy->geom_graphs[level][node].old_physical_x_coord;
1519 *y = hierarchy->geom_graphs[level][node].old_physical_y_coord;
1520 }
1521
1522 /* find_active_ancestor:
1523 * find the 'ancestorIndex' of the active-ancestor of 'node'
1524 * Return negative if node's active_level < level.
1525 */
1526 int
find_active_ancestor(Hierarchy * hierarchy,int level,int node)1527 find_active_ancestor(Hierarchy * hierarchy, int level, int node)
1528 {
1529 int active_level = hierarchy->geom_graphs[level][node].active_level;
1530 while (active_level > level) {
1531 node = hierarchy->v2cv[level][node];
1532 level++;
1533 }
1534
1535 if (active_level == level)
1536 return hierarchy->geom_graphs[level][node].globalIndex;
1537 else
1538 return -1;
1539 }
1540 int
find_old_active_ancestor(Hierarchy * hierarchy,int level,int node)1541 find_old_active_ancestor(Hierarchy * hierarchy, int level, int node)
1542 {
1543 int active_level = hierarchy->geom_graphs[level][node].old_active_level;
1544 while (active_level > level) {
1545 node = hierarchy->v2cv[level][node];
1546 level++;
1547 }
1548
1549 if (active_level == level)
1550 return hierarchy->geom_graphs[level][node].globalIndex;
1551 else
1552 return -1;
1553 }
1554
init_active_level(Hierarchy * hierarchy,int level)1555 void init_active_level(Hierarchy* hierarchy, int level)
1556 {
1557 int i,j;
1558 ex_vtx_data* graph;
1559 for (i=0; i<hierarchy->nlevels; i++) {
1560 graph = hierarchy->geom_graphs[i];
1561 for (j=0; j<hierarchy->nvtxs[i]; j++) {
1562 graph->active_level = level;
1563 graph++;
1564 }
1565 }
1566 }
1567
1568