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