File: compute_hierarchy.c

package info (click to toggle)
graphviz 14.0.5-2
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 139,388 kB
  • sloc: ansic: 141,938; cpp: 11,957; python: 7,766; makefile: 4,043; yacc: 3,030; xml: 2,972; tcl: 2,495; sh: 1,388; objc: 1,159; java: 560; lex: 423; perl: 243; awk: 156; pascal: 139; php: 58; ruby: 49; cs: 31; sed: 1
file content (115 lines) | stat: -rw-r--r-- 3,528 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*************************************************************************
 * Copyright (c) 2011 AT&T Intellectual Property 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: Details at https://graphviz.org
 *************************************************************************/

#include <neatogen/digcola.h>
#include <util/alloc.h>
#ifdef DIGCOLA
#include <neatogen/kkutils.h>

/*
 * This function partitions the graph nodes into levels
 * according to the minimizer of the hierarchy energy.
 * 
 * To allow more flexibility we define a new level only 
 * when the hierarchy energy shows a significant jump
 * (to compensate for noise).
 * This is controlled by two parameters: 'abs_tol' and 
 * 'relative_tol'. The smaller these two are, the more 
 * levels we'll get. 
 * More specifically:
 * We never consider gaps smaller than 'abs_tol'
 * Additionally, we never consider gaps smaller than 'abs_tol'*<avg_gap>
 * 
 * The output is an ordering of the nodes according to 
 * their levels, as follows:
 *   First level: 
 *     ordering[0],ordering[1],...ordering[levels[0]-1]
 *   Second level: 
 *     ordering[levels[0]],ordering[levels[0]+1],...ordering[levels[1]-1]
 *   ...
 *   Last level: 
 *     ordering[levels[num_levels-1]],ordering[levels[num_levels-1]+1],...ordering[n-1]
 * 
 * Hence, the nodes were partitioned into 'num_levels'+1
 * levels.
 * 
 * Please note that 'ordering[levels[i]]' contains the first node at level i+1,
 *  and not the last node of level i.
 */
int
compute_hierarchy(vtx_data * graph, int n, double abs_tol,
		  double relative_tol, double *given_coords,
		  int **orderingp, int **levelsp, int *num_levelsp)
{
    double *y;
    int i, rv=0;
    int *ordering;
    int *levels;
    double tol;			/* node 'i' precedes 'j' in hierarchy iff y[i]-y[j]>tol */
    double hierarchy_span;
    int num_levels;

    /* compute optimizer of hierarchy energy: 'y' */
    if (given_coords) {
	y = given_coords;
    } else {
	y = gv_calloc(n, sizeof(double));
	if (compute_y_coords(graph, n, y, n)) {
	    rv = 1;
	    goto finish;    
	}
    }

    // sort nodes according to their y-ordering
    *orderingp = ordering = gv_calloc(n, sizeof(int));
    for (i = 0; i < n; i++) {
	ordering[i] = i;
    }
    quicksort_place(y, ordering, n);

    /* compute tolerance
     * take the maximum between 'abs_tol' and a fraction of the average gap
     * controlled by 'relative_tol'
     */
    hierarchy_span = y[ordering[n - 1]] - y[ordering[0]];
    tol = MAX(abs_tol, relative_tol * hierarchy_span / (n - 1));
    /* 'hierarchy_span/(n-1)' - average gap between consecutive nodes */


    /* count how many levels the hierarchy contains (a SINGLE_LINK clustering */
    /* alternatively we could use COMPLETE_LINK clustering) */
    num_levels = 0;
    for (i = 1; i < n; i++) {
	if (y[ordering[i]] - y[ordering[i - 1]] > tol) {
	    num_levels++;
	}
    }
    *num_levelsp = num_levels;
    if (num_levels == 0) {
	*levelsp = levels = gv_calloc(1, sizeof(int));
	levels[0] = n;
    } else {
	int count = 0;
	*levelsp = levels = gv_calloc(num_levels, sizeof(int));
	for (i = 1; i < n; i++) {
	    if (y[ordering[i]] - y[ordering[i - 1]] > tol) {
		levels[count++] = i;
	    }
	}
    }
finish:
    if (!given_coords) {
	free(y);
    }

    return rv;
}

#endif				/* DIGCOLA */