Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
hierarchy.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
12// //
13// This file contains the functions //
14// for constructing and managing the //
15// hierarchy structure //
16// //
18
19#include <cgraph/alloc.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <math.h>
24#include <time.h>
25#include <assert.h>
26#include <common/arith.h>
27#include <topfish/hierarchy.h>
28
30// Some utilities for //
31// 'maxmatch(..)' //
33
34static double
35unweighted_common_fraction(v_data * graph, int v, int u, float *v_vector)
36{
37// returns: |N(v) & N(u)| / |N(v) or N(u)|
38// v_vector[i]>0 <==> i is neighbor of v or is v itself
39
40 int neighbor;
41 int num_shared_neighbors = 0;
42 int j;
43 for (j = 0; j < graph[u].nedges; j++) {
44 neighbor = graph[u].edges[j];
45 if (v_vector[neighbor] > 0) {
46 // a shared neighobr
47 num_shared_neighbors++;
48 }
49 }
50 // parallel to the weighted version:
51 //return 2*num_shared_neighbors/(graph[v].nedges+graph[u].nedges);
52
53 // more natural
54 return ((double) num_shared_neighbors) / (graph[v].nedges +
55 graph[u].nedges -
56 num_shared_neighbors);
57}
58
59static void fill_neighbors_vec(v_data *graph, int vtx, float *vtx_vec) {
60 int j;
61 if (graph[0].ewgts != NULL) {
62 for (j = 0; j < graph[vtx].nedges; j++) {
63 vtx_vec[graph[vtx].edges[j]] = fabsf(graph[vtx].ewgts[j]); // use fabsf for the self loop
64 }
65 } else {
66 for (j = 0; j < graph[vtx].nedges; j++) {
67 vtx_vec[graph[vtx].edges[j]] = 1;
68 }
69 }
70}
71
72static void
73fill_neighbors_vec_unweighted(v_data * graph, int vtx, float *vtx_vec)
74{
75 // a node is a neighbor of itself!
76 int j;
77 for (j = 0; j < graph[vtx].nedges; j++) {
78 vtx_vec[graph[vtx].edges[j]] = 1;
79 }
80}
81
82static void empty_neighbors_vec(v_data * graph, int vtx, float *vtx_vec)
83{
84 int j;
85 for (j = 0; j < graph[vtx].nedges; j++) {
86 vtx_vec[graph[vtx].edges[j]] = 0;
87 }
88}
89
90
91static int dist3(v_data * graph, int node1, int node2)
92{
93// succeeds if the graph theoretic distance between the nodes is no more than 3
94 int i, j, k;
95 int u, v;
96 for (i = 1; i < graph[node1].nedges; i++) {
97 u = graph[node1].edges[i];
98 if (u == node2) {
99 return 1;
100 }
101 for (j = 1; j < graph[u].nedges; j++) {
102 v = graph[u].edges[j];
103 if (v == node2) {
104 return 1;
105 }
106 for (k = 1; k < graph[v].nedges; k++) {
107 if (graph[v].edges[k] == node2) {
108 return 1;
109 }
110 }
111 }
112 }
113 return 0;
114}
115
116#define A 1.0
117#define B 1.0
118#define C 3.0
119#define D 1.0
120
121static double ddist(ex_vtx_data * geom_graph, int v, int u)
122{
123// Euclidean distance between nodes 'v' and 'u'
124 double x_v = geom_graph[v].x_coord, y_v = geom_graph[v].y_coord,
125 x_u = geom_graph[u].x_coord, y_u = geom_graph[u].y_coord;
126
127 return hypot(x_v - x_u, y_v - y_u);
128}
129
130extern void quicksort_place(double *, int *, size_t size);
131
132static int
133maxmatch(v_data * graph, /* array of vtx data for graph */
134 ex_vtx_data * geom_graph, /* array of vtx data for graph */
135 int nvtxs, /* number of vertices in graph */
136 int *mflag, /* flag indicating vtx selected or not */
137 int dist2_limit
138 )
139/*
140 Compute a matching of the nodes set.
141 The matching is not based only on the edge list of 'graph',
142 which might be too small,
143 but on the wider edge list of 'geom_graph' (which includes 'graph''s edges)
144
145 We match nodes that are close both in the graph-theoretical sense and
146 in the geometry sense (in the layout)
147*/
148{
149 int *order; /* random ordering of vertices */
150 int *iptr, *jptr; /* loops through integer arrays */
151 int vtx; /* vertex to process next */
152 int neighbor; /* neighbor of a vertex */
153 int nmerged = 0; /* number of edges in matching */
154 int i, j; /* loop counters */
155 float max_norm_edge_weight;
156 double inv_size;
157 double *matchability = gv_calloc(nvtxs, sizeof(double));
158 double min_edge_len;
159 double closest_val = -1, val;
160 int closest_neighbor;
161 float *vtx_vec = gv_calloc(nvtxs, sizeof(float));
162 float *weighted_vtx_vec = gv_calloc(nvtxs, sizeof(float));
163
164 // gather statistics, to enable normalizing the values
165 double avg_edge_len = 0, avg_deg_2 = 0;
166 int nedges = 0;
167
168 for (i = 0; i < nvtxs; i++) {
169 avg_deg_2 += graph[i].nedges;
170 for (j = 1; j < graph[i].nedges; j++) {
171 avg_edge_len += ddist(geom_graph, i, graph[i].edges[j]);
172 nedges++;
173 }
174 }
175 avg_edge_len /= nedges;
176 avg_deg_2 /= nvtxs;
177 avg_deg_2 *= avg_deg_2;
178
179 // the normalized edge weight of edge <v,u> is defined as:
180 // weight(<v,u>)/sqrt(size(v)*size(u))
181 // Now we compute the maximal normalized weight
182 if (graph[0].ewgts != NULL) {
183 max_norm_edge_weight = -1;
184 for (i = 0; i < nvtxs; i++) {
185 inv_size = sqrt(1.0 / geom_graph[i].size);
186 for (j = 1; j < graph[i].nedges; j++) {
187 if (graph[i].ewgts[j] * inv_size /
188 sqrt((float) geom_graph[graph[i].edges[j]].size) >
189 max_norm_edge_weight) {
190 max_norm_edge_weight =
191 (float) (graph[i].ewgts[j] * inv_size /
192 sqrt((double)
193 geom_graph[graph[i].edges[j]].size));
194 }
195 }
196 }
197 } else {
198 max_norm_edge_weight = 1;
199 }
200
201 /* Now determine the order of the vertices. */
202 iptr = order = gv_calloc(nvtxs, sizeof(int));
203 jptr = mflag;
204 for (i = 0; i < nvtxs; i++) {
205 *(iptr++) = i;
206 *(jptr++) = -1;
207 }
208
209 // Option 2: sort the nodes beginning with the ones highly approriate for matching
210
211#ifdef DEBUG
212 srand(0);
213#endif
214 for (i = 0; i < nvtxs; i++) {
215 vtx = order[i];
216 matchability[vtx] = graph[vtx].nedges; // we less want to match high degree nodes
217 matchability[vtx] += geom_graph[vtx].size; // we less want to match large sized nodes
218 min_edge_len = 1e99;
219 for (j = 1; j < graph[vtx].nedges; j++) {
220 min_edge_len =
221 MIN(min_edge_len,
222 ddist(geom_graph, vtx,
223 graph[vtx].edges[j]) / avg_edge_len);
224 }
225 matchability[vtx] += min_edge_len; // we less want to match distant nodes
226 matchability[vtx] += ((double) rand()) / RAND_MAX; // add some randomness
227 }
228 quicksort_place(matchability, order, nvtxs);
229 free(matchability);
230
231 // Start determining the matched pairs
232 for (i = 0; i < nvtxs; i++) {
233 vtx_vec[i] = 0;
234 }
235 for (i = 0; i < nvtxs; i++) {
236 weighted_vtx_vec[i] = 0;
237 }
238
239 // relative weights of the different criteria
240 for (i = 0; i < nvtxs; i++) {
241 vtx = order[i];
242 if (mflag[vtx] >= 0) { /* already matched. */
243 continue;
244 }
245 inv_size = sqrt(1.0 / geom_graph[vtx].size);
246 fill_neighbors_vec(graph, vtx, weighted_vtx_vec);
248 closest_neighbor = -1;
249
250 /*
251 We match node i with the "closest" neighbor, based on 4 criteria:
252 (1) (Weighted) fraction of common neighbors (measured on orig. graph)
253 (2) AvgDeg*AvgDeg/(deg(vtx)*deg(neighbor)) (degrees measured on orig. graph)
254 (3) AvgEdgeLen/dist(vtx,neighbor)
255 (4) Weight of normalized direct connection between nodes (measured on orig. graph)
256 */
257
258 for (j = 1; j < geom_graph[vtx].nedges; j++) {
259 neighbor = geom_graph[vtx].edges[j];
260 if (mflag[neighbor] >= 0) { /* already matched. */
261 continue;
262 }
263 // (1):
264 val =
266 vtx_vec);
267
268 if (val == 0 && (dist2_limit || !dist3(graph, vtx, neighbor))) {
269 // graph theoretical distance is larger than 3 (or 2 if '!dist3(graph, vtx, neighbor)' is commented)
270 // nodes cannot be matched
271 continue;
272 }
273 // (2)
274 val +=
275 B * avg_deg_2 / (graph[vtx].nedges *
276 graph[neighbor].nedges);
277
278
279 // (3)
280 val += C * avg_edge_len / ddist(geom_graph, vtx, neighbor);
281
282 // (4)
283 val +=
284 (weighted_vtx_vec[neighbor] * inv_size /
285 sqrt((float) geom_graph[neighbor].size)) /
286 max_norm_edge_weight;
287
288
289
290 if (val > closest_val || closest_neighbor == -1) {
291 closest_neighbor = neighbor;
292 closest_val = val;
293 }
294
295 }
296 if (closest_neighbor != -1) {
297 mflag[vtx] = closest_neighbor;
298 mflag[closest_neighbor] = vtx;
299 nmerged++;
300 }
301 empty_neighbors_vec(graph, vtx, vtx_vec);
302 empty_neighbors_vec(graph, vtx, weighted_vtx_vec);
303 }
304
305 free(order);
306 free(vtx_vec);
307 free(weighted_vtx_vec);
308 return (nmerged);
309}
310
311/* Construct mapping from original graph nodes to coarsened graph nodes */
312static void makev2cv(int *mflag, /* flag indicating vtx selected or not */
313 int nvtxs, /* number of vtxs in original graph */
314 int *v2cv, /* mapping from vtxs to coarsened vtxs */
315 int *cv2v /* mapping from coarsened vtxs to vtxs */
316 )
317{
318 int i, j; /* loop counters */
319
320 j = 0;
321 for (i = 0; i < nvtxs; i++) {
322 if (mflag[i] < 0) { // unmatched node
323 v2cv[i] = j;
324 cv2v[2 * j] = i;
325 cv2v[2 * j + 1] = -1;
326 j++;
327 } else if (mflag[i] > i) { // matched node
328 v2cv[i] = j;
329 v2cv[mflag[i]] = j;
330 cv2v[2 * j] = i;
331 cv2v[2 * j + 1] = mflag[i];
332 j++;
333 }
334 }
335}
336
337static int make_coarse_graph(v_data * graph, /* array of vtx data for graph */
338 int nedges, /* number of edges in graph */
339 v_data ** cgp, /* coarsened version of graph */
340 int cnvtxs, /* number of vtxs in coarsened graph */
341 int *v2cv, /* mapping from vtxs to coarsened vtxs */
342 int *cv2v /* mapping from coarsened vtxs to vtxs */
343 )
344// This function takes the information about matched pairs
345// and use it to contract these pairs and build a coarse graph
346{
347 int j, cv, v, neighbor, cv_nedges;
348 int cnedges = 0; /* number of edges in coarsened graph */
349 v_data *cgraph; /* coarsened version of graph */
350 int *index = gv_calloc(cnvtxs, sizeof(int));
351 float intra_weight;
352 /* An upper bound on the number of coarse graph edges. */
353 int maxCnedges = nedges; // do not subtract (nvtxs-cnvtxs) because we do not contract only along edges
354 int *edges;
355 float *eweights;
356
357 /* Now allocate space for the new graph. Overeallocate and realloc later. */
358 cgraph = gv_calloc(cnvtxs, sizeof(v_data));
359 edges = gv_calloc(2 * maxCnedges + cnvtxs, sizeof(int));
360 eweights = gv_calloc(2 * maxCnedges + cnvtxs, sizeof(float));
361
362 if (graph[0].ewgts != NULL) {
363 // use edge weights
364 for (cv = 0; cv < cnvtxs; cv++) {
365
366 intra_weight = 0;
367
368 cgraph[cv].edges = edges;
369 cgraph[cv].ewgts = eweights;
370
371 cv_nedges = 1;
372 v = cv2v[2 * cv];
373 for (j = 1; j < graph[v].nedges; j++) {
374 neighbor = v2cv[graph[v].edges[j]];
375 if (neighbor == cv) {
376 intra_weight = 2 * graph[v].ewgts[j]; // count both directions of the intra-edge
377 continue;
378 }
379 if (index[neighbor] == 0) { // new neighbor
380 index[neighbor] = cv_nedges;
381 cgraph[cv].edges[cv_nedges] = neighbor;
382 cgraph[cv].ewgts[cv_nedges] = graph[v].ewgts[j];
383 cv_nedges++;
384 } else {
385 cgraph[cv].ewgts[index[neighbor]] += graph[v].ewgts[j];
386 }
387 }
388
389 cgraph[cv].ewgts[0] = graph[v].ewgts[0];
390
391 if ((v = cv2v[2 * cv + 1]) != -1) {
392 for (j = 1; j < graph[v].nedges; j++) {
393 neighbor = v2cv[graph[v].edges[j]];
394 if (neighbor == cv)
395 continue;
396 if (index[neighbor] == 0) { // new neighbor
397 index[neighbor] = cv_nedges;
398 cgraph[cv].edges[cv_nedges] = neighbor;
399 cgraph[cv].ewgts[cv_nedges] = graph[v].ewgts[j];
400 cv_nedges++;
401 } else {
402 cgraph[cv].ewgts[index[neighbor]] +=
403 graph[v].ewgts[j];
404 }
405 }
406 cgraph[cv].ewgts[0] += graph[v].ewgts[0] + intra_weight;
407 }
408 cgraph[cv].nedges = cv_nedges;
409 cgraph[cv].edges[0] = cv;
410 edges += cv_nedges;
411 eweights += cv_nedges;
412 cnedges += cv_nedges;
413
414 for (j = 1; j < cgraph[cv].nedges; j++)
415 index[cgraph[cv].edges[j]] = 0;
416 }
417 } else { // fine graph is unweighted
418 int internal_weight = 0;
419
420 for (cv = 0; cv < cnvtxs; cv++) {
421
422 cgraph[cv].edges = edges;
423 cgraph[cv].ewgts = eweights;
424
425 cv_nedges = 1;
426 v = cv2v[2 * cv];
427 for (j = 1; j < graph[v].nedges; j++) {
428 neighbor = v2cv[graph[v].edges[j]];
429 if (neighbor == cv) {
430 internal_weight = 2;
431 continue;
432 }
433 if (index[neighbor] == 0) { // new neighbor
434 index[neighbor] = cv_nedges;
435 cgraph[cv].edges[cv_nedges] = neighbor;
436 cgraph[cv].ewgts[cv_nedges] = -1;
437 cv_nedges++;
438 } else {
439 cgraph[cv].ewgts[index[neighbor]]--;
440 }
441 }
442 cgraph[cv].ewgts[0] = (float) graph[v].edges[0]; // this is our trick to store the weights on the diag in an unweighted graph
443 if ((v = cv2v[2 * cv + 1]) != -1) {
444 for (j = 1; j < graph[v].nedges; j++) {
445 neighbor = v2cv[graph[v].edges[j]];
446 if (neighbor == cv)
447 continue;
448 if (index[neighbor] == 0) { // new neighbor
449 index[neighbor] = cv_nedges;
450 cgraph[cv].edges[cv_nedges] = neighbor;
451 cgraph[cv].ewgts[cv_nedges] = -1;
452 cv_nedges++;
453 } else {
454 cgraph[cv].ewgts[index[neighbor]]--;
455 }
456 }
457 // we subtract the weight of the intra-edge that was counted twice
458 cgraph[cv].ewgts[0] +=
459 (float) graph[v].edges[0] - internal_weight;
460 // In a case the edge weights are defined as positive:
461 //cgraph[cv].ewgts[0] += (float) graph[v].edges[0]+internal_weight;
462 }
463
464 cgraph[cv].nedges = cv_nedges;
465 cgraph[cv].edges[0] = cv;
466 edges += cv_nedges;
467 eweights += cv_nedges;
468 cnedges += cv_nedges;
469
470 for (j = 1; j < cgraph[cv].nedges; j++)
471 index[cgraph[cv].edges[j]] = 0;
472 }
473 }
474 cnedges -= cnvtxs;
475 cnedges /= 2;
476 free(index);
477 *cgp = cgraph;
478 return cnedges;
479}
480
481static int
483 ex_vtx_data * graph, /* array of vtx data for graph */
484 int nedges, /* number of edges in graph */
485 ex_vtx_data ** cgp, /* coarsened version of graph */
486 int cnvtxs, /* number of vtxs in coarsened graph */
487 int *v2cv, /* mapping from vtxs to coarsened vtxs */
488 int *cv2v /* mapping from coarsened vtxs to vtxs */
489)
490// This function takes the information about matched pairs
491// and use it to contract these pairs and build a coarse ex_graph
492{
493 int cnedges; /* number of edges in coarsened graph */
494 ex_vtx_data *cgraph; /* coarsened version of graph */
495 int j, cv, v, neighbor, cv_nedges;
496 int *index = gv_calloc(cnvtxs, sizeof(int));
497 int *edges;
498
499 /* An upper bound on the number of coarse graph edges. */
500 cnedges = nedges;
501
502 /* Now allocate space for the new graph. Overeallocate and realloc later. */
503 cgraph = gv_calloc(cnvtxs, sizeof(ex_vtx_data));
504 edges = gv_calloc(2 * cnedges + cnvtxs, sizeof(int));
505
506 for (cv = 0; cv < cnvtxs; cv++) {
507
508 cgraph[cv].edges = edges;
509
510 cv_nedges = 1;
511 v = cv2v[2 * cv];
512 for (j = 1; j < graph[v].nedges; j++) {
513 neighbor = v2cv[graph[v].edges[j]];
514 if (neighbor == cv) {
515 continue;
516 }
517 if (index[neighbor] == 0) { // new neighbor
518 index[neighbor] = cv_nedges;
519 cgraph[cv].edges[cv_nedges] = neighbor;
520 cv_nedges++;
521 }
522 }
523 cgraph[cv].size = graph[v].size;
524 cgraph[cv].x_coord = graph[v].x_coord;
525 cgraph[cv].y_coord = graph[v].y_coord;
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 cv_nedges++;
535 }
536 }
537 // compute new coord's as a weighted average of the old ones
538 cgraph[cv].x_coord =
539 (cgraph[cv].size * cgraph[cv].x_coord +
540 graph[v].size * graph[v].x_coord) / (cgraph[cv].size +
541 graph[v].size);
542 cgraph[cv].y_coord =
543 (cgraph[cv].size * cgraph[cv].y_coord +
544 graph[v].size * graph[v].y_coord) / (cgraph[cv].size +
545 graph[v].size);
546 cgraph[cv].size += graph[v].size;
547 }
548 cgraph[cv].nedges = cv_nedges;
549 cgraph[cv].edges[0] = cv;
550 edges += cv_nedges;
551
552 for (j = 1; j < cgraph[cv].nedges; j++)
553 index[cgraph[cv].edges[j]] = 0;
554 }
555 free(index);
556 *cgp = cgraph;
557 return cnedges;
558}
559
560static void
562 v_data * graph, /* graph to be matched */
563 ex_vtx_data* geom_graph, /* another graph (with coords) on the same nodes */
564 int nvtxs, /* number of vertices in graph */
565 int nedges, /* number of edges in graph */
566 int geom_nedges, /* number of edges in geom_graph */
567 v_data ** cgraph, /* coarsened version of graph */
568 ex_vtx_data ** cgeom_graph, /* coarsened version of geom_graph */
569 int *cnp, /* number of vtxs in coarsened graph */
570 int *cnedges, /* number of edges in coarsened graph */
571 int *cgeom_nedges, /* number of edges in coarsened geom_graph */
572 int **v2cvp, /* reference from vertices to coarse vertices */
573 int **cv2vp, /* reference from vertices to coarse vertices */
574 int dist2_limit
575)
576
577/*
578 * This function gets two graphs with the same node set and
579 * constructs two corresponding coarsened graphs of about
580 * half the size
581 */
582{
583 int *mflag; /* flag indicating vtx matched or not */
584 int nmerged; /* number of edges contracted */
585 int *v2cv; /* reference from vertices to coarse vertices */
586 int *cv2v; /* reference from vertices to coarse vertices */
587 int cnvtxs;
588
589 /* Allocate and initialize space. */
590 mflag = gv_calloc(nvtxs, sizeof(int));
591
592 /* Find a maximal matching in the graphs */
593 nmerged = maxmatch(graph, geom_graph, nvtxs, mflag, dist2_limit);
594
595 /* Now construct coarser graph by contracting along matching edges. */
596 /* Pairs of values in mflag array indicate matched vertices. */
597 /* A negative value indicates that vertex is unmatched. */
598
599 *cnp = cnvtxs = nvtxs - nmerged;
600
601 *v2cvp = v2cv = gv_calloc(nvtxs, sizeof(int));
602 *cv2vp = cv2v = gv_calloc(2 * cnvtxs, sizeof(int));
603 makev2cv(mflag, nvtxs, v2cv, cv2v);
604
605 free(mflag);
606
607 *cnedges = make_coarse_graph(graph, nedges, cgraph, cnvtxs, v2cv, cv2v);
608 *cgeom_nedges = make_coarse_ex_graph(geom_graph, geom_nedges, cgeom_graph,
609 cnvtxs, v2cv, cv2v);
610}
611
612static v_data *cpGraph(v_data * graph, int n, int nedges)
613{
614 float *ewgts = NULL;
615 int i, j;
616
617 if (graph == NULL || n == 0) {
618 return NULL;
619 }
620 v_data *cpGraph = gv_calloc(n, sizeof(v_data));
621 int *edges = gv_calloc(2 * nedges + n, sizeof(int));
622 if (graph[0].ewgts != NULL) {
623 ewgts = gv_calloc(2 * nedges + n, sizeof(float));
624 }
625
626 for (i = 0; i < n; i++) {
627 cpGraph[i] = graph[i];
628 cpGraph[i].edges = edges;
629 cpGraph[i].ewgts = ewgts;
630 for (j = 0; j < graph[i].nedges; j++) {
631 edges[j] = graph[i].edges[j];
632 }
633 edges += graph[i].nedges;
634 if (ewgts != NULL) {
635 for (j = 0; j < graph[i].nedges; j++) {
636 ewgts[j] = graph[i].ewgts[j];
637 }
638 ewgts += graph[i].nedges;
639 }
640 }
641 return cpGraph;
642}
643
645{
646 int i, j;
647
648 if (graph == NULL || n == 0) {
649 return NULL;
650 }
652 int *edges = gv_calloc(2 * nedges + n, sizeof(int));
653
654 for (i = 0; i < n; i++) {
655 cpGraph[i] = graph[i];
656 cpGraph[i].edges = edges;
657 for (j = 0; j < graph[i].nedges; j++) {
658 edges[j] = graph[i].edges[j];
659 }
660 edges += graph[i].nedges;
661 }
662 return cpGraph;
663}
664
666 ex_vtx_data * geom_graph, int ngeom_edges,
668{
669 int cur_level;
670 Hierarchy *hierarchy = gv_alloc(sizeof(Hierarchy));
671 int cngeom_edges = ngeom_edges;
672 ex_vtx_data *geom_graph_level;
673 int nodeIndex = 0;
674 int i, j;
675 static const int min_nvtxs = 20;
676 int nlevels = MAX(5, 10 * (int) log((float) (nvtxs / min_nvtxs))); // just an estimate
677
678 hierarchy->graphs = gv_calloc(nlevels, sizeof(v_data*));
679 hierarchy->geom_graphs = gv_calloc(nlevels, sizeof(ex_vtx_data*));
680 hierarchy->nvtxs = gv_calloc(nlevels, sizeof(int));
681 hierarchy->nedges = gv_calloc(nlevels, sizeof(int));
682 hierarchy->v2cv = gv_calloc(nlevels, sizeof(int*));
683 hierarchy->cv2v = gv_calloc(nlevels, sizeof(int*));
684
685 hierarchy->graphs[0] = cpGraph(graph, nvtxs, nedges);
686 hierarchy->geom_graphs[0] = cpExGraph(geom_graph, nvtxs, ngeom_edges);
687 hierarchy->nvtxs[0] = nvtxs;
688 hierarchy->nedges[0] = nedges;
689
690 for (cur_level = 0;
691 hierarchy->nvtxs[cur_level] > min_nvtxs
692 && cur_level < 50 /*nvtxs/10 */ ; cur_level++) {
693 if (cur_level == nlevels - 1) { // we have to allocate more space
694 hierarchy->graphs =
695 gv_recalloc(hierarchy->graphs, nlevels, nlevels * 2, sizeof(v_data*));
696 hierarchy->geom_graphs =
697 gv_recalloc(hierarchy->geom_graphs, nlevels, nlevels * 2, sizeof(ex_vtx_data*));
698 hierarchy->nvtxs = gv_recalloc(hierarchy->nvtxs, nlevels, nlevels * 2, sizeof(int));
699 hierarchy->nedges = gv_recalloc(hierarchy->nedges, nlevels, nlevels * 2, sizeof(int));
700 hierarchy->v2cv = gv_recalloc(hierarchy->v2cv, nlevels, nlevels * 2, sizeof(int*));
701 hierarchy->cv2v = gv_recalloc(hierarchy->cv2v, nlevels, nlevels * 2, sizeof(int*));
702 nlevels *= 2;
703 }
704
705 ngeom_edges = cngeom_edges;
707 (hierarchy->graphs[cur_level],
708 hierarchy->geom_graphs[cur_level],
709 hierarchy->nvtxs[cur_level], hierarchy->nedges[cur_level],
710 ngeom_edges, &hierarchy->graphs[cur_level + 1],
711 &hierarchy->geom_graphs[cur_level + 1],
712 &hierarchy->nvtxs[cur_level + 1],
713 &hierarchy->nedges[cur_level + 1], &cngeom_edges,
714 &hierarchy->v2cv[cur_level], &hierarchy->cv2v[cur_level + 1],
715 parms->dist2_limit);
716 }
717
718 hierarchy->nlevels = cur_level + 1;
719
720 // assign consecutive global identifiers to all nodes on hierarchy
721 for (i = 0; i < hierarchy->nlevels; i++) {
722 geom_graph_level = hierarchy->geom_graphs[i];
723 for (j = 0; j < hierarchy->nvtxs[i]; j++) {
724 geom_graph_level[j].globalIndex = nodeIndex;
725 nodeIndex++;
726 }
727 }
728 hierarchy->maxNodeIndex = nodeIndex;
729 return hierarchy;
730}
731
732static double
733dist_from_foci(ex_vtx_data * graph, int node, int *foci, int num_foci)
734{
735// compute minimum distance of 'node' from the set 'foci'
736 int i;
737 double distance = ddist(graph, node, foci[0]);
738 for (i = 1; i < num_foci; i++) {
739 distance = MIN(distance, ddist(graph, node, foci[i]));
740 }
741
742 return distance;
743}
744
745/* set_active_levels:
746 * Compute the "active level" field of each node in the hierarchy.
747 * Note that if the active level is lower than the node's level, the node
748 * is "split" in the presentation; if the active level is higher than
749 * the node's level, then the node is aggregated into a coarser node.
750 * If the active level equals the node's level then the node is currently shown
751 */
752void
753set_active_levels(Hierarchy * hierarchy, int *foci_nodes, int num_foci,
755{
756 int n, i;
757 int *nodes;
758 double *distances;
760 int level;
761 int group_size;
762 int thresh;
763 int vtx;
764 ex_vtx_data *cgraph;
765 int *cv2v;
766 int v, u;
767 int min_level = 0;
768
769 graph = hierarchy->geom_graphs[min_level]; // finest graph
770 n = hierarchy->nvtxs[min_level];
771
772 // compute distances from foci nodes
773 nodes = gv_calloc(n, sizeof(int));
774 distances = gv_calloc(n, sizeof(double));
775 for (i = 0; i < n; i++) {
776 nodes[i] = i;
777 distances[i] = dist_from_foci(graph, i, foci_nodes, num_foci);
778 }
779
780 // sort nodes according to their distance from foci
781 quicksort_place(distances, nodes, n);
782
783 /* compute *desired* levels of fine nodes by distributing them into buckets
784 * The sizes of the buckets is a geometric series with
785 * factor: 'coarsening_rate'
786 */
787 level = min_level;
788 group_size = parms->num_fine_nodes * num_foci;
789 thresh = group_size;
790 for (i = 0; i < n; i++) {
791 vtx = nodes[i];
792 if (i > thresh && level < hierarchy->nlevels - 1) {
793 level++;
794 group_size = (int) (group_size * parms->coarsening_rate);
795 thresh += group_size;
796 }
797 graph[vtx].active_level = level;
798 }
799
800 // Fine-to-coarse sweep:
801 //----------------------
802 // Propagate levels to all coarse nodes and determine final levels
803 // at lowest meeting points. Note that nodes can be active in
804 // lower (finer) levels than what originally desired, since if 'u'
805 // and 'v' are merged, than the active level of '{u,v}' will be
806 // the minimum of the active levels of 'u' and 'v'
807 for (level = min_level + 1; level < hierarchy->nlevels; level++) {
808 cgraph = hierarchy->geom_graphs[level];
809 graph = hierarchy->geom_graphs[level - 1];
810 cv2v = hierarchy->cv2v[level];
811 n = hierarchy->nvtxs[level];
812 for (i = 0; i < n; i++) {
813 v = cv2v[2 * i];
814 u = cv2v[2 * i + 1];
815 if (u >= 0) { // cv is decomposed from 2 fine nodes
816 if (graph[v].active_level < level
817 || graph[u].active_level < level) {
818 // At least one of the nodes should be active at a lower level,
819 // in this case both children are active at a lower level
820 // and we don't wait till they are merged
821 graph[v].active_level =
822 MIN(graph[v].active_level, level - 1);
823 graph[u].active_level =
824 MIN(graph[u].active_level, level - 1);
825 }
826 // The node with the finer (lower) active level determines the coarse active level
827 cgraph[i].active_level =
828 MIN(graph[v].active_level, graph[u].active_level);
829 } else {
830 cgraph[i].active_level = graph[v].active_level;
831 }
832 }
833 }
834
835 // Coarse-to-fine sweep:
836 //----------------------
837 // Propagate final levels all the way to fine nodes
838 for (level = hierarchy->nlevels - 1; level > 0; level--) {
839 cgraph = hierarchy->geom_graphs[level];
840 graph = hierarchy->geom_graphs[level - 1];
841 cv2v = hierarchy->cv2v[level];
842 n = hierarchy->nvtxs[level];
843 for (i = 0; i < n; i++) {
844 if (cgraph[i].active_level < level) {
845 continue;
846 }
847 // active level has been already reached, copy level to children
848 v = cv2v[2 * i];
849 u = cv2v[2 * i + 1];
850 graph[v].active_level = cgraph[i].active_level;
851 if (u >= 0) {
852 graph[u].active_level = cgraph[i].active_level;
853 }
854 }
855 }
856 free(nodes);
857 free(distances);
858}
859
860/* findClosestActiveNode:
861 * Given (x,y) in physical coords, check if node is closer to this point
862 * than previous setting. If so, reset values.
863 * If node is not active, recurse down finer levels.
864 * Return closest distance squared.
865 */
866static double
868 int level, double x, double y,
869 double closest_dist, int *closest_node,
870 int *closest_node_level)
871{
873
874 graph = hierarchy->geom_graphs[level];
875
876 if (graph[node].active_level == level)
877 { // node is active
878 double delx = x - graph[node].physical_x_coord;
879 double dely = y - graph[node].physical_y_coord;
880 double dist = delx*delx + dely*dely;
881
882 if (dist < closest_dist)
883 {
884 closest_dist = dist;
885 *closest_node = node;
886 *closest_node_level = level;
887
888
889 }
890 return closest_dist;
891 }
892
893 closest_dist =
894 findClosestActiveNode(hierarchy, hierarchy->cv2v[level][2 * node],
895 level - 1, x, y, closest_dist, closest_node,
896 closest_node_level);
897
898 if (hierarchy->cv2v[level][2 * node + 1] >= 0) {
899 closest_dist =
900 findClosestActiveNode(hierarchy,
901 hierarchy->cv2v[level][2 * node + 1],
902 level - 1, x, y, closest_dist,
903 closest_node, closest_node_level);
904 }
905 return closest_dist;
906}
907
908/* find_leftmost_descendant:
909 * Given coarse node in given level, return representative node
910 * in lower level cur_level.
911 */
912static int
913find_leftmost_descendant(Hierarchy * hierarchy, int node, int level,
914 int cur_level)
915{
916 while (level > cur_level)
917 {
918 node = hierarchy->cv2v[level--][2 * node];
919 }
920 return node;
921}
922
923/* find_closest_active_node:
924 * Given x and y in physical coordinate system, determine closest
925 * actual node in graph. Store this in closest_fine_node, and return
926 * distance squared.
927 */
928double
929find_closest_active_node(Hierarchy * hierarchy, double x, double y,
930 int *closest_fine_node)
931{
932 int i, closest_node, closest_node_level;
933 int top_level = hierarchy->nlevels - 1;
934 double min_dist = 1e20;
935
936 for (i = 0; i < hierarchy->nvtxs[top_level]; i++)
937 {
938 min_dist = findClosestActiveNode(hierarchy, i, top_level, x, y,min_dist, &closest_node, &closest_node_level);
939 }
940 *closest_fine_node =find_leftmost_descendant(hierarchy, closest_node,closest_node_level, 0);
941
942 return min_dist;
943}
944
945int
946init_ex_graph(v_data * graph1, v_data * graph2, int n,
947 double *x_coords, double *y_coords, ex_vtx_data ** gp)
948{
949 // build ex_graph from the union of edges in 'graph1' and 'graph2'
950 // note that this function does not destroy the input graphs
951
952 ex_vtx_data *geom_graph;
953 int nedges1 = 0, nedges2 = 0;
954 int nedges = 0;
955 int i, j, k, l, first_nedges;
956 int neighbor;
957 for (i = 0; i < n; i++) {
958 nedges1 += graph1[i].nedges;
959 nedges2 += graph2[i].nedges;
960 }
961 int *edges = gv_calloc(nedges1 + nedges2, sizeof(int));
962 *gp = geom_graph = gv_calloc(n, sizeof(ex_vtx_data));
963
964 for (i = 0; i < n; i++) {
965 geom_graph[i].edges = edges;
966 geom_graph[i].size = 1;
967 geom_graph[i].x_coord = (float) x_coords[i];
968 geom_graph[i].y_coord = (float) y_coords[i];
969 geom_graph[i].edges[0] = i;
970 for (j = 1; j < graph1[i].nedges; j++) {
971 edges[j] = graph1[i].edges[j];
972 }
973 first_nedges = k = graph1[i].nedges;
974 for (j = 1; j < graph2[i].nedges; j++) {
975 neighbor = graph2[i].edges[j];
976 for (l = 1; l < first_nedges; l++) {
977 if (edges[l] == neighbor) { // already existed neighbor
978 break;
979 }
980 }
981 if (l == first_nedges) { // neighbor wasn't found
982 edges[k++] = neighbor;
983 }
984 }
985 geom_graph[i].nedges = k;
986 edges += k;
987 nedges += k;
988 }
989 nedges /= 2;
990 return nedges;
991}
992
993/* extract_active_logical_coords:
994 * Preorder scan the hierarchy tree, and extract the logical coordinates of
995 * all active nodes
996 * Store (universal) coords in x_coords and y_coords and increment index.
997 * Return index.
998 */
999size_t extract_active_logical_coords(Hierarchy *hierarchy, int node, int level,
1000 double *x_coords, double *y_coords,
1001 size_t counter) {
1002
1003 ex_vtx_data *graph = hierarchy->geom_graphs[level];
1004
1005 if (graph[node].active_level == level) { // node is active
1006 x_coords[counter] = graph[node].x_coord;
1007 y_coords[counter++] = graph[node].y_coord;
1008 return counter;
1009 }
1010
1011 counter =
1013 hierarchy->cv2v[level][2 * node],
1014 level - 1, x_coords, y_coords,
1015 counter);
1016
1017 if (hierarchy->cv2v[level][2 * node + 1] >= 0) {
1018 counter =
1020 hierarchy->cv2v[level][2 * node +
1021 1],
1022 level - 1, x_coords, y_coords,
1023 counter);
1024 }
1025 return counter;
1026}
1027
1028/* set_active_physical_coords:
1029 * Preorder scan the hierarchy tree, and set the physical coordinates
1030 * of all active nodes
1031 */
1032int
1033set_active_physical_coords(Hierarchy * hierarchy, int node, int level,
1034 double *x_coords, double *y_coords, int counter)
1035{
1036
1037 ex_vtx_data *graph = hierarchy->geom_graphs[level];
1038
1039 if (graph[node].active_level == level) { // node is active
1040 graph[node].physical_x_coord = (float) x_coords[counter];
1041 graph[node].physical_y_coord = (float) y_coords[counter++];
1042 return counter;
1043 }
1044
1045 counter =
1047 hierarchy->cv2v[level][2*node],
1048 level - 1, x_coords, y_coords, counter);
1049
1050 if (hierarchy->cv2v[level][2 * node + 1] >= 0) {
1051 counter =
1053 hierarchy->cv2v[level][2*node + 1],
1054 level - 1, x_coords, y_coords,
1055 counter);
1056 }
1057 return counter;
1058}
1059
1060/* find_physical_coords:
1061 * find the 'physical_coords' of the active-ancestor of 'node'
1062 */
1063void
1064find_physical_coords(Hierarchy * hierarchy, int level, int node, double *x,
1065 double *y)
1066{
1067 int active_level = hierarchy->geom_graphs[level][node].active_level;
1068 while (active_level > level) {
1069 node = hierarchy->v2cv[level][node];
1070 level++;
1071 }
1072
1073 *x = hierarchy->geom_graphs[level][node].physical_x_coord;
1074 *y = hierarchy->geom_graphs[level][node].physical_y_coord;
1075}
1076
1077void
1078find_active_ancestor_info(Hierarchy * hierarchy, int level, int node, int *levell,int *nodee)
1079{
1080 int active_level = hierarchy->geom_graphs[level][node].active_level;
1081 while (active_level > level) {
1082 node = hierarchy->v2cv[level][node];
1083 level++;
1084 }
1085
1086 *nodee = node;
1087 *levell = level;
1088}
1089
1090
1091
1092
1093/* find_old_physical_coords:
1094 * find the 'old_physical_coords' of the old active-ancestor of 'node'
1095 */
1096void
1097find_old_physical_coords(Hierarchy * hierarchy, int level, int node, double *x,
1098 double *y)
1099{
1100 int active_level = hierarchy->geom_graphs[level][node].old_active_level;
1101 while (active_level > level) {
1102 node = hierarchy->v2cv[level][node];
1103 level++;
1104 }
1105
1106 *x = hierarchy->geom_graphs[level][node].old_physical_x_coord;
1107 *y = hierarchy->geom_graphs[level][node].old_physical_y_coord;
1108}
1109
1110/* find_active_ancestor:
1111 * find the 'ancestorIndex' of the active-ancestor of 'node'
1112 * Return negative if node's active_level < level.
1113 */
1114int
1115find_active_ancestor(Hierarchy * hierarchy, int level, int node)
1116{
1117 int active_level = hierarchy->geom_graphs[level][node].active_level;
1118 while (active_level > level) {
1119 node = hierarchy->v2cv[level][node];
1120 level++;
1121 }
1122
1123 if (active_level == level)
1124 return hierarchy->geom_graphs[level][node].globalIndex;
1125 else
1126 return -1;
1127}
1128
1129void init_active_level(Hierarchy* hierarchy, int level)
1130{
1131 int i,j;
1133 for (i=0; i<hierarchy->nlevels; i++) {
1134 graph = hierarchy->geom_graphs[i];
1135 for (j=0; j<hierarchy->nvtxs[i]; j++) {
1136 graph->active_level = level;
1137 graph++;
1138 }
1139 }
1140}
1141
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define MIN(a, b)
Definition arith.h:28
static double dist(int dim, double *x, double *y)
double distance(double *x, int dim, int i, int j)
Definition general.c:145
void free(void *)
node NULL
Definition grammar.y:149
Agraph_t * graph(char *name)
Definition gv.cpp:31
Hierarchy * create_hierarchy(v_data *graph, int nvtxs, int nedges, ex_vtx_data *geom_graph, int ngeom_edges, hierparms_t *parms)
Definition hierarchy.c:665
static double findClosestActiveNode(Hierarchy *hierarchy, int node, int level, double x, double y, double closest_dist, int *closest_node, int *closest_node_level)
Definition hierarchy.c:867
#define B
Definition hierarchy.c:117
void set_active_levels(Hierarchy *hierarchy, int *foci_nodes, int num_foci, levelparms_t *parms)
Definition hierarchy.c:753
static double unweighted_common_fraction(v_data *graph, int v, int u, float *v_vector)
Definition hierarchy.c:35
void quicksort_place(double *, int *, size_t size)
size_t extract_active_logical_coords(Hierarchy *hierarchy, int node, int level, double *x_coords, double *y_coords, size_t counter)
Definition hierarchy.c:999
static void fill_neighbors_vec_unweighted(v_data *graph, int vtx, float *vtx_vec)
Definition hierarchy.c:73
static double dist_from_foci(ex_vtx_data *graph, int node, int *foci, int num_foci)
Definition hierarchy.c:733
int find_active_ancestor(Hierarchy *hierarchy, int level, int node)
Definition hierarchy.c:1115
int set_active_physical_coords(Hierarchy *hierarchy, int node, int level, double *x_coords, double *y_coords, int counter)
Definition hierarchy.c:1033
static int maxmatch(v_data *graph, ex_vtx_data *geom_graph, int nvtxs, int *mflag, int dist2_limit)
Definition hierarchy.c:133
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)
Definition hierarchy.c:561
static void fill_neighbors_vec(v_data *graph, int vtx, float *vtx_vec)
Definition hierarchy.c:59
double find_closest_active_node(Hierarchy *hierarchy, double x, double y, int *closest_fine_node)
Definition hierarchy.c:929
void find_active_ancestor_info(Hierarchy *hierarchy, int level, int node, int *levell, int *nodee)
Definition hierarchy.c:1078
static double ddist(ex_vtx_data *geom_graph, int v, int u)
Definition hierarchy.c:121
void init_active_level(Hierarchy *hierarchy, int level)
Definition hierarchy.c:1129
static void empty_neighbors_vec(v_data *graph, int vtx, float *vtx_vec)
Definition hierarchy.c:82
static int make_coarse_ex_graph(ex_vtx_data *graph, int nedges, ex_vtx_data **cgp, int cnvtxs, int *v2cv, int *cv2v)
Definition hierarchy.c:482
static ex_vtx_data * cpExGraph(ex_vtx_data *graph, int n, int nedges)
Definition hierarchy.c:644
#define A
Definition hierarchy.c:116
static void makev2cv(int *mflag, int nvtxs, int *v2cv, int *cv2v)
Definition hierarchy.c:312
void find_old_physical_coords(Hierarchy *hierarchy, int level, int node, double *x, double *y)
Definition hierarchy.c:1097
static int make_coarse_graph(v_data *graph, int nedges, v_data **cgp, int cnvtxs, int *v2cv, int *cv2v)
Definition hierarchy.c:337
#define C
Definition hierarchy.c:118
static v_data * cpGraph(v_data *graph, int n, int nedges)
Definition hierarchy.c:612
static int dist3(v_data *graph, int node1, int node2)
Definition hierarchy.c:91
void find_physical_coords(Hierarchy *hierarchy, int level, int node, double *x, double *y)
Definition hierarchy.c:1064
int init_ex_graph(v_data *graph1, v_data *graph2, int n, double *x_coords, double *y_coords, ex_vtx_data **gp)
Definition hierarchy.c:946
static int find_leftmost_descendant(Hierarchy *hierarchy, int node, int level, int cur_level)
Definition hierarchy.c:913
#define neighbor(t, i, edim, elist)
Definition make_map.h:37
static int nedges
total no. of edges used in routing
Definition routespl.c:31
v_data ** graphs
Definition hierarchy.h:40
int * nvtxs
Definition hierarchy.h:42
int ** cv2v
Definition hierarchy.h:51
int * nedges
Definition hierarchy.h:43
ex_vtx_data ** geom_graphs
Definition hierarchy.h:41
int nlevels
Definition hierarchy.h:39
int ** v2cv
Definition hierarchy.h:46
int maxNodeIndex
Definition hierarchy.h:52
float physical_y_coord
Definition hierarchy.h:29
float old_physical_y_coord
Definition hierarchy.h:32
float old_physical_x_coord
Definition hierarchy.h:31
int old_active_level
Definition hierarchy.h:33
float physical_x_coord
Definition hierarchy.h:28
int globalIndex
Definition hierarchy.h:21
float x_coord
Definition hierarchy.h:24
int active_level
Definition hierarchy.h:20
float y_coord
Definition hierarchy.h:25
int * edges
Definition hierarchy.h:18
int nedges
Definition sparsegraph.h:22
int * edges
Definition sparsegraph.h:23
float * ewgts
Definition sparsegraph.h:24
static parms_t parms
Definition tlayout.c:82
#define MAX(a, b)
Definition write.c:31