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