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