/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/neatogen/constrained_majorization_ipsep.c

Go to the documentation of this file.
00001 /* $Id: constrained_majorization_ipsep.c,v 1.5 2007/11/12 22:24:27 erg Exp $ $Revision: 1.5 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00020 /**********************************************************
00021  * Based on constrained_majorization.c
00022  *
00023  * Perform stress majorization subject
00024  * to separation constraints, for background see the paper:
00025  * "IPSep-CoLa: An Incremental Procedure for Separation Constraint Layout of Graphs"
00026  * by Tim Dwyer, Yehuda Koren and Kim Marriott
00027  *
00028  * Available separation constraints so far are:
00029  *  o Directed edge constraints
00030  *  o Node non-overlap constraints
00031  *  o Cluster containment constraints
00032  *  o Cluster/node non-overlap constraints
00033  *
00034  * Tim Dwyer, 2006
00035  **********************************************************/
00036 
00037 #include "digcola.h"
00038 #ifdef IPSEPCOLA
00039 #include <math.h>
00040 #include <stdlib.h>
00041 #include <time.h>
00042 #include <stdio.h>
00043 #include <float.h>
00044 #include "stress.h"
00045 #include "dijkstra.h"
00046 #include "bfs.h"
00047 #include "matrix_ops.h"
00048 #include "kkutils.h"
00049 #include "conjgrad.h"
00050 #include <csolve_VPSC.h>
00051 #include "quad_prog_vpsc.h"
00052 #include "quad_prog_solver.h"
00053 #include "matrix_ops.h"
00054 
00055 #define localConstrMajorIterations 1000
00056 
00057 int stress_majorization_cola(
00058     vtx_data * graph,   /* Input graph in sparse representation  */
00059     int n,              /* Number of nodes */
00060     int nedges_graph,   /* Number of edges */
00061     double **d_coords,  /* Coordinates of nodes (output layout)  */
00062     int dim,            /* Dimemsionality of layout */
00063     int model,          /* difference model */
00064     int maxi,           /* max iterations */
00065     ipsep_options * opt)
00066 {
00067     int iterations = 0;   /* Output: number of iteration of the process */
00068 
00069         /*************************************************
00070         ** Computation of full, dense, unrestricted k-D ** 
00071         ** stress minimization by majorization          ** 
00072         ** This function imposes HIERARCHY CONSTRAINTS  **
00073         *************************************************/
00074 
00075     int i, j, k;
00076     float *lap1 = NULL;
00077     float *dist_accumulator = NULL;
00078     float *tmp_coords = NULL;
00079     float **b = NULL;
00080     double *degrees = NULL;
00081     float *lap2 = NULL;
00082     int lap_length;
00083     float *f_storage = NULL;
00084     float **coords = NULL;
00085     int orig_n = n;
00086 
00087     /*double conj_tol=tolerance_cg; *//* tolerance of Conjugate Gradient */
00088     CMajEnvVPSC *cMajEnvHor = NULL;
00089     CMajEnvVPSC *cMajEnvVrt = NULL;
00090     double y_0;
00091     int length;
00092     DistType diameter;
00093     float *Dij = NULL;
00094     float constant_term;
00095     int count;
00096     double degree;
00097     int step;
00098     float val;
00099     double old_stress, new_stress = 0;
00100     boolean converged;
00101     int len;
00102     double nsizeScale = 0;
00103     float maxEdgeLen = 0;
00104     double max = 1;
00105 
00106     initLayout(graph, n, dim, d_coords);
00107     if (n == 1)
00108         return 0;
00109 
00110     for (i = 0; i < n; i++) {
00111         for (j = 1; j < graph[i].nedges; j++) {
00112             maxEdgeLen = MAX(graph[i].ewgts[j], maxEdgeLen);
00113         }
00114     }
00115 
00116         /****************************************************
00117         ** Compute the all-pairs-shortest-distances matrix **
00118         ****************************************************/
00119 
00120     if (maxi == 0)
00121         return iterations;
00122 
00123     if (model == MODEL_SUBSET) {
00124         /* weight graph to separate high-degree nodes */
00125         /* and perform slower Dijkstra-based computation */
00126         if (Verbose)
00127             fprintf(stderr, "Calculating subset model");
00128         Dij = compute_apsp_artifical_weights_packed(graph, n);
00129     } else if (model == MODEL_CIRCUIT) {
00130         Dij = circuitModel(graph, n);
00131         if (!Dij) {
00132             agerr(AGWARN,
00133                   "graph is disconnected. Hence, the circuit model\n");
00134             agerr(AGPREV,
00135                   "is undefined. Reverting to the shortest path model.\n");
00136         }
00137     }
00138     if (!Dij) {
00139         if (Verbose)
00140             fprintf(stderr, "Calculating shortest paths\n ");
00141         Dij = compute_apsp_packed(graph, n);
00142     }
00143 
00144     diameter = -1;
00145     length = n + n * (n - 1) / 2;
00146     for (i = 0; i < length; i++) {
00147         if (Dij[i] > diameter) {
00148             diameter = (int) Dij[i];
00149         }
00150     }
00151 
00152     /* for numerical stability, scale down layout                */
00153     /* No Jiggling, might conflict with constraints                      */
00154     for (i = 0; i < dim; i++) {
00155         for (j = 0; j < n; j++) {
00156             if (fabs(d_coords[i][j]) > max) {
00157                 max = fabs(d_coords[i][j]);
00158             }
00159         }
00160     }
00161     for (i = 0; i < dim; i++) {
00162         for (j = 0; j < n; j++) {
00163             d_coords[i][j] *= 10 / max;
00164         }
00165     }
00166 
00167         /**************************
00168         ** Layout initialization **
00169         **************************/
00170 
00171     for (i = 0; i < dim; i++) {
00172         orthog1(n, d_coords[i]);
00173     }
00174 
00175     /* for the y-coords, don't center them, but translate them so y[0]=0 */
00176     y_0 = d_coords[1][0];
00177     for (i = 0; i < n; i++) {
00178         d_coords[1][i] -= y_0;
00179     }
00180 
00181         /**************************
00182         ** Laplacian computation **
00183         **************************/
00184 
00185     lap2 = Dij;
00186     lap_length = n + n * (n - 1) / 2;
00187     square_vec(lap_length, lap2);
00188     /* compute off-diagonal entries */
00189     invert_vec(lap_length, lap2);
00190 
00191     if (opt->clusters->nclusters > 0) {
00192         int nn = n + opt->clusters->nclusters * 2;
00193         int clap_length = nn + nn * (nn - 1) / 2;
00194         float *clap = N_GNEW(clap_length, float);
00195         int c0, c1;
00196         float v;
00197         c0 = c1 = 0;
00198         for (i = 0; i < nn; i++) {
00199             for (j = 0; j < nn - i; j++) {
00200                 if (i < n && j < n - i) {
00201                     v = lap2[c0++];
00202                 } else {
00203                     /* v=j==1?i%2:0; */
00204                     if (j == 1 && i % 2 == 1) {
00205                         v = maxEdgeLen;
00206                         v *= v;
00207                         if (v > 0.01) {
00208                             v = 1.0 / v;
00209                         }
00210                     } else
00211                         v = 0;
00212                 }
00213                 clap[c1++] = v;
00214             }
00215         }
00216         free(lap2);
00217         lap2 = clap;
00218         n = nn;
00219         lap_length = clap_length;
00220     }
00221     /* compute diagonal entries */
00222     count = 0;
00223     degrees = N_GNEW(n, double);
00224     set_vector_val(n, 0, degrees);
00225     for (i = 0; i < n - 1; i++) {
00226         degree = 0;
00227         count++;                /* skip main diag entry */
00228         for (j = 1; j < n - i; j++, count++) {
00229             val = lap2[count];
00230             degree += val;
00231             degrees[i + j] -= val;
00232         }
00233         degrees[i] -= degree;
00234     }
00235     for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) {
00236         lap2[count] = (float) degrees[i];
00237     }
00238 
00239     coords = N_GNEW(dim, float *);
00240     f_storage = N_GNEW(dim * n, float);
00241     for (i = 0; i < dim; i++) {
00242         coords[i] = f_storage + i * n;
00243         for (j = 0; j < n; j++) {
00244             coords[i][j] = j < orig_n ? (float) (d_coords[i][j]) : 0;
00245         }
00246     }
00247 
00248     /* compute constant term in stress sum
00249      * which is \sum_{i<j} w_{ij}d_{ij}^2
00250      */
00251     constant_term = (float) (n * (n - 1) / 2);
00252 
00253         /*************************
00254         ** Layout optimization  **
00255         *************************/
00256 
00257     b = N_GNEW(dim, float *);
00258     b[0] = N_GNEW(dim * n, float);
00259     for (k = 1; k < dim; k++) {
00260         b[k] = b[0] + k * n;
00261     }
00262 
00263     tmp_coords = N_GNEW(n, float);
00264     dist_accumulator = N_GNEW(n, float);
00265 
00266     old_stress = DBL_MAX;       /* at least one iteration */
00267 
00268     cMajEnvHor = initCMajVPSC(n, lap2, graph, opt, 0);
00269     cMajEnvVrt = initCMajVPSC(n, lap2, graph, opt, opt->diredges);
00270 
00271     lap1 = N_GNEW(lap_length, float);
00272 
00273     for (converged = FALSE, iterations = 0;
00274          iterations < maxi && !converged; iterations++) {
00275 
00276         /* First, construct Laplacian of 1/(d_ij*|p_i-p_j|)  */
00277         set_vector_val(n, 0, degrees);
00278         sqrt_vecf(lap_length, lap2, lap1);
00279         for (count = 0, i = 0; i < n - 1; i++) {
00280             len = n - i - 1;
00281             /* init 'dist_accumulator' with zeros */
00282             set_vector_valf(n, 0, dist_accumulator);
00283 
00284             /* put into 'dist_accumulator' all squared distances 
00285              * between 'i' and 'i'+1,...,'n'-1
00286              */
00287             for (k = 0; k < dim; k++) {
00288                 set_vector_valf(len, coords[k][i], tmp_coords);
00289                 vectors_mult_additionf(len, tmp_coords, -1,
00290                                        coords[k] + i + 1);
00291                 square_vec(len, tmp_coords);
00292                 vectors_additionf(len, tmp_coords, dist_accumulator,
00293                                   dist_accumulator);
00294             }
00295 
00296             /* convert to 1/d_{ij} */
00297             invert_sqrt_vec(len, dist_accumulator);
00298             /* detect overflows */
00299             for (j = 0; j < len; j++) {
00300                 if (dist_accumulator[j] >= FLT_MAX
00301                     || dist_accumulator[j] < 0) {
00302                     dist_accumulator[j] = 0;
00303                 }
00304             }
00305 
00306             count++;            /* save place for the main diagonal entry */
00307             degree = 0;
00308             for (j = 0; j < len; j++, count++) {
00309                 val = lap1[count] *= dist_accumulator[j];
00310                 degree += val;
00311                 degrees[i + j + 1] -= val;
00312             }
00313             degrees[i] -= degree;
00314         }
00315         for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) {
00316             lap1[count] = (float) degrees[i];
00317         }
00318 
00319         /* Now compute b[] (L^(X(t))*X(t)) */
00320         for (k = 0; k < dim; k++) {
00321             /* b[k] := lap1*coords[k] */
00322             right_mult_with_vector_ff(lap1, n, coords[k], b[k]);
00323         }
00324 
00325         /* compute new stress
00326          * remember that the Laplacians are negated, so we subtract 
00327          * instead of add and vice versa
00328          */
00329         new_stress = 0;
00330         for (k = 0; k < dim; k++) {
00331             new_stress += vectors_inner_productf(n, coords[k], b[k]);
00332         }
00333         new_stress *= 2;
00334         new_stress += constant_term;    /* only after mult by 2 */
00335         for (k = 0; k < dim; k++) {
00336             right_mult_with_vector_ff(lap2, n, coords[k], tmp_coords);
00337             new_stress -= vectors_inner_productf(n, coords[k], tmp_coords);
00338         }
00339 
00340 #ifdef ALTERNATIVE_STRESS_CALC
00341         {
00342             double mat_stress = new_stress;
00343             double compute_stress(float **coords, float *lap, int dim,
00344                                   int n);
00345             new_stress = compute_stress(coords, lap2, dim, n);
00346             if (fabs(mat_stress - new_stress) /
00347                 min(mat_stress, new_stress) > 0.001) {
00348                 fprintf(stderr,
00349                         "Diff stress vals: %lf %lf (iteration #%d)\n",
00350                         mat_stress, new_stress, iterations);
00351             }
00352         }
00353 #endif
00354         /* check for convergence */
00355         if (Verbose && (iterations % 1 == 0)) {
00356             fprintf(stderr, "%.3f ", new_stress);
00357             if (iterations % 10 == 0)
00358                 fprintf(stderr, "\n");
00359         }
00360         converged = new_stress < old_stress
00361             && fabs(new_stress - old_stress) / fabs(old_stress + 1e-10) <
00362             Epsilon;
00363         /*converged = converged || (iterations>1 && new_stress>old_stress); */
00364         /* in first iteration we allowed stress increase, which 
00365          * might result ny imposing constraints
00366          */
00367         old_stress = new_stress;
00368 
00369         /* in determining non-overlap constraints we gradually scale up the
00370          * size of nodes to avoid local minima
00371          */
00372         if ((iterations >= maxi - 1 || converged) && opt->noverlap == 1
00373             && nsizeScale < 0.999) {
00374             nsizeScale += 0.1;
00375             if (Verbose)
00376                 fprintf(stderr, "nsizescale=%f,iterations=%d\n",
00377                         nsizeScale, iterations);
00378             iterations = 0;
00379             converged = FALSE;
00380         }
00381 
00382 
00383         /* now we find the optimizer of trace(X'LX)+X'B by solving 'dim' 
00384          * system of equations, thereby obtaining the new coordinates.
00385          * If we use the constraints (given by the var's: 'ordering', 
00386          * 'levels' and 'num_levels'), we cannot optimize 
00387          * trace(X'LX)+X'B by simply solving equations, but we have
00388          * to use a quadratic programming solver
00389          * note: 'lap2' is a packed symmetric matrix, that is its 
00390          * upper-triangular part is arranged in a vector row-wise
00391          * also note: 'lap2' is really the negated laplacian (the 
00392          * laplacian is -'lap2')
00393          */
00394 
00395         if (opt->noverlap == 1 && nsizeScale > 0.001) {
00396             generateNonoverlapConstraints(cMajEnvHor, nsizeScale, coords,
00397                                           0,
00398                                           nsizeScale < 0.5 ? FALSE : TRUE,
00399                                           opt);
00400         }
00401         if (cMajEnvHor->m > 0) {
00402 #ifdef MOSEK
00403             if (opt->mosek) {
00404                 mosek_quad_solve_sep(cMajEnvHor->mosekEnv, n, b[0],
00405                                      coords[0]);
00406             } else
00407 #endif                          /* MOSEK */
00408                 constrained_majorization_vpsc(cMajEnvHor, b[0], coords[0],
00409                                               localConstrMajorIterations);
00410         } else {
00411             /* if there are no constraints then use conjugate gradient
00412              * optimisation which should be considerably faster
00413              */
00414             conjugate_gradient_mkernel(lap2, coords[0], b[0], n,
00415                                        tolerance_cg, n);
00416         }
00417         if (opt->noverlap == 1 && nsizeScale > 0.001) {
00418             generateNonoverlapConstraints(cMajEnvVrt, nsizeScale, coords,
00419                                           1, FALSE, opt);
00420         }
00421         if (cMajEnvVrt->m > 0) {
00422 #ifdef MOSEK
00423             if (opt->mosek) {
00424                 mosek_quad_solve_sep(cMajEnvVrt->mosekEnv, n, b[1],
00425                                      coords[1]);
00426             } else
00427 #endif                          /* MOSEK */
00428                 constrained_majorization_vpsc(cMajEnvVrt, b[1], coords[1],
00429                                               localConstrMajorIterations);
00430         } else {
00431             conjugate_gradient_mkernel(lap2, coords[1], b[1], n,
00432                                        tolerance_cg, n);
00433         }
00434     }
00435     if (Verbose) {
00436         fprintf(stderr, "\nfinal e = %f %d iterations %.2f sec\n",
00437                 new_stress, iterations, elapsed_sec());
00438     }
00439     deleteCMajEnvVPSC(cMajEnvHor);
00440     deleteCMajEnvVPSC(cMajEnvVrt);
00441 
00442     if (opt->noverlap == 2) {
00443         /* fprintf(stderr, "Removing overlaps as post-process...\n"); */
00444         removeoverlaps(orig_n, coords, opt);
00445     }
00446 
00447     if (coords != NULL) {
00448         for (i = 0; i < dim; i++) {
00449             for (j = 0; j < orig_n; j++) {
00450                 d_coords[i][j] = coords[i][j];
00451             }
00452         }
00453         free(coords[0]);
00454         free(coords);
00455     }
00456 
00457     if (b) {
00458         free(b[0]);
00459         free(b);
00460     }
00461     free(tmp_coords);
00462     free(dist_accumulator);
00463     free(degrees);
00464     free(lap2);
00465     free(lap1);
00466 
00467     return iterations;
00468 }
00469 #endif                          /* IPSEPCOLA */

Generated on Mon Mar 31 19:03:27 2008 for Graphviz by  doxygen 1.5.1