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