Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
compute_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
11#include <cgraph/alloc.h>
12#include <neatogen/digcola.h>
13#ifdef DIGCOLA
14#include <neatogen/kkutils.h>
15
16/*
17 * This function partitions the graph nodes into levels
18 * according to the minimizer of the hierarchy energy.
19 *
20 * To allow more flexibility we define a new level only
21 * when the hierarchy energy shows a significant jump
22 * (to compensate for noise).
23 * This is controlled by two parameters: 'abs_tol' and
24 * 'relative_tol'. The smaller these two are, the more
25 * levels we'll get.
26 * More speciffically:
27 * We never consider gaps smaller than 'abs_tol'
28 * Additionally, we never consider gaps smaller than 'abs_tol'*<avg_gap>
29 *
30 * The output is an ordering of the nodes according to
31 * their levels, as follows:
32 * First level:
33 * ordering[0],ordering[1],...ordering[levels[0]-1]
34 * Second level:
35 * ordering[levels[0]],ordering[levels[0]+1],...ordering[levels[1]-1]
36 * ...
37 * Last level:
38 * ordering[levels[num_levels-1]],ordering[levels[num_levels-1]+1],...ordering[n-1]
39 *
40 * Hence, the nodes were partitioned into 'num_levels'+1
41 * levels.
42 *
43 * Please note that 'ordering[levels[i]]' contains the first node at level i+1,
44 * and not the last node of level i.
45 */
46int
47compute_hierarchy(vtx_data * graph, int n, double abs_tol,
48 double relative_tol, double *given_coords,
49 int **orderingp, int **levelsp, int *num_levelsp)
50{
51 double *y;
52 int i, rv=0;
53 int *ordering;
54 int *levels;
55 double tol; /* node 'i' precedes 'j' in hierarchy iff y[i]-y[j]>tol */
56 double hierarchy_span;
57 int num_levels;
58
59 /* compute optimizer of hierarchy energy: 'y' */
60 if (given_coords) {
61 y = given_coords;
62 } else {
63 y = gv_calloc(n, sizeof(double));
64 if (compute_y_coords(graph, n, y, n)) {
65 rv = 1;
66 goto finish;
67 }
68 }
69
70 /* sort nodes accoridng to their y-ordering */
71 *orderingp = ordering = gv_calloc(n, sizeof(int));
72 for (i = 0; i < n; i++) {
73 ordering[i] = i;
74 }
75 quicksort_place(y, ordering, n);
76
77 /* compute tolerance
78 * take the maximum between 'abs_tol' and a fraction of the average gap
79 * controlled by 'relative_tol'
80 */
81 hierarchy_span = y[ordering[n - 1]] - y[ordering[0]];
82 tol = MAX(abs_tol, relative_tol * hierarchy_span / (n - 1));
83 /* 'hierarchy_span/(n-1)' - average gap between consecutive nodes */
84
85
86 /* count how many levels the hierarchy contains (a SINGLE_LINK clustering */
87 /* alternatively we could use COMPLETE_LINK clustering) */
88 num_levels = 0;
89 for (i = 1; i < n; i++) {
90 if (y[ordering[i]] - y[ordering[i - 1]] > tol) {
91 num_levels++;
92 }
93 }
94 *num_levelsp = num_levels;
95 if (num_levels == 0) {
96 *levelsp = levels = gv_calloc(1, sizeof(int));
97 levels[0] = n;
98 } else {
99 int count = 0;
100 *levelsp = levels = gv_calloc(num_levels, sizeof(int));
101 for (i = 1; i < n; i++) {
102 if (y[ordering[i]] - y[ordering[i - 1]] > tol) {
103 levels[count++] = i;
104 }
105 }
106 }
107finish:
108 if (!given_coords) {
109 free(y);
110 }
111
112 return rv;
113}
114
115#endif /* DIGCOLA */
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
void free(void *)
Agraph_t * graph(char *name)
Definition gv.cpp:31
void quicksort_place(double *place, int *ordering, int size)
Definition kkutils.c:161
static const double tol
#define MAX(a, b)
Definition write.c:31