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