Graphviz  2.29.20120523.0446
lib/neatogen/compute_hierarchy.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 #include <digcola.h>
00015 #ifdef DIGCOLA
00016 #include "kkutils.h"
00017 
00018 static int *given_levels = NULL;
00019 /*
00020  * This function partitions the graph nodes into levels
00021  * according to the minimizer of the hierarchy energy.
00022  * 
00023  * To allow more flexibility we define a new level only 
00024  * when the hierarchy energy shows a significant jump
00025  * (to compensate for noise).
00026  * This is controlled by two parameters: 'abs_tol' and 
00027  * 'relative_tol'. The smaller these two are, the more 
00028  * levels we'll get. 
00029  * More speciffically:
00030  * We never consider gaps smaller than 'abs_tol'
00031  * Additionally, we never consider gaps smaller than 'abs_tol'*<avg_gap>
00032  * 
00033  * The output is an ordering of the nodes according to 
00034  * their levels, as follows:
00035  *   First level: 
00036  *     ordering[0],ordering[1],...ordering[levels[0]-1]
00037  *   Second level: 
00038  *     ordering[levels[0]],ordering[levels[0]+1],...ordering[levels[1]-1]
00039  *   ...
00040  *   Last level: 
00041  *     ordering[levels[num_levels-1]],ordering[levels[num_levels-1]+1],...ordering[n-1]
00042  * 
00043  * Hence, the nodes were partitioned into 'num_levels'+1
00044  * levels.
00045  * 
00046  * Please note that 'ordering[levels[i]]' contains the first node at level i+1,
00047  *  and not the last node of level i.
00048  */
00049 int
00050 compute_hierarchy(vtx_data * graph, int n, double abs_tol,
00051                   double relative_tol, double *given_coords,
00052                   int **orderingp, int **levelsp, int *num_levelsp)
00053 {
00054     double *y;
00055     int i, rv=0;
00056     double spread;
00057     int use_given_levels = FALSE;
00058     int *ordering;
00059     int *levels;
00060     double tol;                 /* node 'i' precedes 'j' in hierachy iff y[i]-y[j]>tol */
00061     double hierarchy_span;
00062     int num_levels;
00063 
00064     /* compute optimizer of hierarchy energy: 'y' */
00065     if (given_coords) {
00066         y = given_coords;
00067     } else {
00068         y = N_GNEW(n, double);
00069         if (compute_y_coords(graph, n, y, n)) {
00070             rv = 1;
00071             goto finish;    
00072         }
00073     }
00074 
00075     /* sort nodes accoridng to their y-ordering */
00076     *orderingp = ordering = N_NEW(n, int);
00077     for (i = 0; i < n; i++) {
00078         ordering[i] = i;
00079     }
00080     quicksort_place(y, ordering, 0, n - 1);
00081 
00082     spread = y[ordering[n - 1]] - y[ordering[0]];
00083 
00084     /* after spread is computed, we may take the y-coords as the given levels */
00085     if (given_levels) {
00086         use_given_levels = TRUE;
00087         for (i = 0; i < n; i++) {
00088             use_given_levels = use_given_levels && given_levels[i] >= 0;
00089         }
00090     }
00091     if (use_given_levels) {
00092         for (i = 0; i < n; i++) {
00093             y[i] = given_levels[i];
00094             ordering[i] = i;
00095         }
00096         quicksort_place(y, ordering, 0, n - 1);
00097     }
00098 
00099     /* compute tolerance
00100      * take the maximum between 'abs_tol' and a fraction of the average gap
00101      * controlled by 'relative_tol'
00102      */
00103     hierarchy_span = y[ordering[n - 1]] - y[ordering[0]];
00104     tol = MAX(abs_tol, relative_tol * hierarchy_span / (n - 1));
00105     /* 'hierarchy_span/(n-1)' - average gap between consecutive nodes */
00106 
00107 
00108     /* count how many levels the hierarchy contains (a SINGLE_LINK clustering */
00109     /* alternatively we could use COMPLETE_LINK clustering) */
00110     num_levels = 0;
00111     for (i = 1; i < n; i++) {
00112         if (y[ordering[i]] - y[ordering[i - 1]] > tol) {
00113             num_levels++;
00114         }
00115     }
00116     *num_levelsp = num_levels;
00117     if (num_levels == 0) {
00118         *levelsp = levels = N_GNEW(1, int);
00119         levels[0] = n;
00120     } else {
00121         int count = 0;
00122         *levelsp = levels = N_GNEW(num_levels, int);
00123         for (i = 1; i < n; i++) {
00124             if (y[ordering[i]] - y[ordering[i - 1]] > tol) {
00125                 levels[count++] = i;
00126             }
00127         }
00128     }
00129 finish:
00130     if (!given_coords) {
00131         free(y);
00132     }
00133 
00134     return rv;
00135 }
00136 
00137 #endif                          /* DIGCOLA */