Graphviz  2.29.20120523.0446
lib/neatogen/constrained_majorization_ipsep.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
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(vtx_data * graph,  /* Input graph in sparse representation  */
00058                              int n,     /* Number of nodes */
00059                              int nedges_graph,  /* Number of edges */
00060                              double **d_coords, /* Coordinates of nodes (output layout)  */
00061                              node_t ** nodes,   /* Original nodes */
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, nodes);
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 (Verbose)
00124         start_timer();
00125 
00126     if (model == MODEL_SUBSET) {
00127         /* weight graph to separate high-degree nodes */
00128         /* and perform slower Dijkstra-based computation */
00129         if (Verbose)
00130             fprintf(stderr, "Calculating subset model");
00131         Dij = compute_apsp_artifical_weights_packed(graph, n);
00132     } else if (model == MODEL_CIRCUIT) {
00133         Dij = circuitModel(graph, n);
00134         if (!Dij) {
00135             agerr(AGWARN,
00136                   "graph is disconnected. Hence, the circuit model\n");
00137             agerr(AGPREV,
00138                   "is undefined. Reverting to the shortest path model.\n");
00139         }
00140     } else if (model == MODEL_MDS) {
00141         if (Verbose)
00142             fprintf(stderr, "Calculating MDS model");
00143         Dij = mdsModel(graph, n);
00144     }
00145     if (!Dij) {
00146         if (Verbose)
00147             fprintf(stderr, "Calculating shortest paths");
00148         Dij = compute_apsp_packed(graph, n);
00149     }
00150     if (Verbose) {
00151         fprintf(stderr, ": %.2f sec\n", elapsed_sec());
00152         fprintf(stderr, "Setting initial positions");
00153         start_timer();
00154     }
00155 
00156     diameter = -1;
00157     length = n + n * (n - 1) / 2;
00158     for (i = 0; i < length; i++) {
00159         if (Dij[i] > diameter) {
00160             diameter = (int) Dij[i];
00161         }
00162     }
00163 
00164     /* for numerical stability, scale down layout                */
00165     /* No Jiggling, might conflict with constraints                      */
00166     for (i = 0; i < dim; i++) {
00167         for (j = 0; j < n; j++) {
00168             if (fabs(d_coords[i][j]) > max) {
00169                 max = fabs(d_coords[i][j]);
00170             }
00171         }
00172     }
00173     for (i = 0; i < dim; i++) {
00174         for (j = 0; j < n; j++) {
00175             d_coords[i][j] *= 10 / max;
00176         }
00177     }
00178 
00179         /**************************
00180         ** Layout initialization **
00181         **************************/
00182 
00183     for (i = 0; i < dim; i++) {
00184         orthog1(n, d_coords[i]);
00185     }
00186 
00187     /* for the y-coords, don't center them, but translate them so y[0]=0 */
00188     y_0 = d_coords[1][0];
00189     for (i = 0; i < n; i++) {
00190         d_coords[1][i] -= y_0;
00191     }
00192     if (Verbose)
00193         fprintf(stderr, ": %.2f sec", elapsed_sec());
00194 
00195         /**************************
00196         ** Laplacian computation **
00197         **************************/
00198 
00199     lap2 = Dij;
00200     lap_length = n + n * (n - 1) / 2;
00201     square_vec(lap_length, lap2);
00202     /* compute off-diagonal entries */
00203     invert_vec(lap_length, lap2);
00204 
00205     if (opt->clusters->nclusters > 0) {
00206         int nn = n + opt->clusters->nclusters * 2;
00207         int clap_length = nn + nn * (nn - 1) / 2;
00208         float *clap = N_GNEW(clap_length, float);
00209         int c0, c1;
00210         float v;
00211         c0 = c1 = 0;
00212         for (i = 0; i < nn; i++) {
00213             for (j = 0; j < nn - i; j++) {
00214                 if (i < n && j < n - i) {
00215                     v = lap2[c0++];
00216                 } else {
00217                     /* v=j==1?i%2:0; */
00218                     if (j == 1 && i % 2 == 1) {
00219                         v = maxEdgeLen;
00220                         v *= v;
00221                         if (v > 0.01) {
00222                             v = 1.0 / v;
00223                         }
00224                     } else
00225                         v = 0;
00226                 }
00227                 clap[c1++] = v;
00228             }
00229         }
00230         free(lap2);
00231         lap2 = clap;
00232         n = nn;
00233         lap_length = clap_length;
00234     }
00235     /* compute diagonal entries */
00236     count = 0;
00237     degrees = N_GNEW(n, double);
00238     set_vector_val(n, 0, degrees);
00239     for (i = 0; i < n - 1; i++) {
00240         degree = 0;
00241         count++;                /* skip main diag entry */
00242         for (j = 1; j < n - i; j++, count++) {
00243             val = lap2[count];
00244             degree += val;
00245             degrees[i + j] -= val;
00246         }
00247         degrees[i] -= degree;
00248     }
00249     for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) {
00250         lap2[count] = (float) degrees[i];
00251     }
00252 
00253     coords = N_GNEW(dim, float *);
00254     f_storage = N_GNEW(dim * n, float);
00255     for (i = 0; i < dim; i++) {
00256         coords[i] = f_storage + i * n;
00257         for (j = 0; j < n; j++) {
00258             coords[i][j] = j < orig_n ? (float) (d_coords[i][j]) : 0;
00259         }
00260     }
00261 
00262     /* compute constant term in stress sum
00263      * which is \sum_{i<j} w_{ij}d_{ij}^2
00264      */
00265     constant_term = (float) (n * (n - 1) / 2);
00266 
00267         /*************************
00268         ** Layout optimization  **
00269         *************************/
00270 
00271     b = N_GNEW(dim, float *);
00272     b[0] = N_GNEW(dim * n, float);
00273     for (k = 1; k < dim; k++) {
00274         b[k] = b[0] + k * n;
00275     }
00276 
00277     tmp_coords = N_GNEW(n, float);
00278     dist_accumulator = N_GNEW(n, float);
00279 
00280     old_stress = DBL_MAX;       /* at least one iteration */
00281 
00282     if ((cMajEnvHor = initCMajVPSC(n, lap2, graph, opt, 0)) == NULL) {
00283         iterations = -1;
00284         goto finish;
00285     }
00286     if ((cMajEnvVrt = initCMajVPSC(n, lap2, graph, opt, opt->diredges)) == NULL) {
00287         iterations = -1;
00288         goto finish;
00289     }
00290 
00291     lap1 = N_GNEW(lap_length, float);
00292 
00293     for (converged = FALSE, iterations = 0;
00294          iterations < maxi && !converged; iterations++) {
00295 
00296         /* First, construct Laplacian of 1/(d_ij*|p_i-p_j|)  */
00297         set_vector_val(n, 0, degrees);
00298         sqrt_vecf(lap_length, lap2, lap1);
00299         for (count = 0, i = 0; i < n - 1; i++) {
00300             len = n - i - 1;
00301             /* init 'dist_accumulator' with zeros */
00302             set_vector_valf(n, 0, dist_accumulator);
00303 
00304             /* put into 'dist_accumulator' all squared distances 
00305              * between 'i' and 'i'+1,...,'n'-1
00306              */
00307             for (k = 0; k < dim; k++) {
00308                 set_vector_valf(len, coords[k][i], tmp_coords);
00309                 vectors_mult_additionf(len, tmp_coords, -1,
00310                                        coords[k] + i + 1);
00311                 square_vec(len, tmp_coords);
00312                 vectors_additionf(len, tmp_coords, dist_accumulator,
00313                                   dist_accumulator);
00314             }
00315 
00316             /* convert to 1/d_{ij} */
00317             invert_sqrt_vec(len, dist_accumulator);
00318             /* detect overflows */
00319             for (j = 0; j < len; j++) {
00320                 if (dist_accumulator[j] >= FLT_MAX
00321                     || dist_accumulator[j] < 0) {
00322                     dist_accumulator[j] = 0;
00323                 }
00324             }
00325 
00326             count++;            /* save place for the main diagonal entry */
00327             degree = 0;
00328             for (j = 0; j < len; j++, count++) {
00329                 val = lap1[count] *= dist_accumulator[j];
00330                 degree += val;
00331                 degrees[i + j + 1] -= val;
00332             }
00333             degrees[i] -= degree;
00334         }
00335         for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) {
00336             lap1[count] = (float) degrees[i];
00337         }
00338 
00339         /* Now compute b[] (L^(X(t))*X(t)) */
00340         for (k = 0; k < dim; k++) {
00341             /* b[k] := lap1*coords[k] */
00342             right_mult_with_vector_ff(lap1, n, coords[k], b[k]);
00343         }
00344 
00345         /* compute new stress
00346          * remember that the Laplacians are negated, so we subtract 
00347          * instead of add and vice versa
00348          */
00349         new_stress = 0;
00350         for (k = 0; k < dim; k++) {
00351             new_stress += vectors_inner_productf(n, coords[k], b[k]);
00352         }
00353         new_stress *= 2;
00354         new_stress += constant_term;    /* only after mult by 2 */
00355         for (k = 0; k < dim; k++) {
00356             right_mult_with_vector_ff(lap2, n, coords[k], tmp_coords);
00357             new_stress -= vectors_inner_productf(n, coords[k], tmp_coords);
00358         }
00359 
00360 #ifdef ALTERNATIVE_STRESS_CALC
00361         {
00362             double mat_stress = new_stress;
00363             double compute_stress(float **coords, float *lap, int dim,
00364                                   int n);
00365             new_stress = compute_stress(coords, lap2, dim, n);
00366             if (fabs(mat_stress - new_stress) /
00367                 min(mat_stress, new_stress) > 0.001) {
00368                 fprintf(stderr,
00369                         "Diff stress vals: %lf %lf (iteration #%d)\n",
00370                         mat_stress, new_stress, iterations);
00371             }
00372         }
00373 #endif
00374         /* check for convergence */
00375         if (Verbose && (iterations % 1 == 0)) {
00376             fprintf(stderr, "%.3f ", new_stress);
00377             if (iterations % 10 == 0)
00378                 fprintf(stderr, "\n");
00379         }
00380         converged = new_stress < old_stress
00381             && fabs(new_stress - old_stress) / fabs(old_stress + 1e-10) <
00382             Epsilon;
00383         /*converged = converged || (iterations>1 && new_stress>old_stress); */
00384         /* in first iteration we allowed stress increase, which 
00385          * might result ny imposing constraints
00386          */
00387         old_stress = new_stress;
00388 
00389         /* in determining non-overlap constraints we gradually scale up the
00390          * size of nodes to avoid local minima
00391          */
00392         if ((iterations >= maxi - 1 || converged) && opt->noverlap == 1
00393             && nsizeScale < 0.999) {
00394             nsizeScale += 0.1;
00395             if (Verbose)
00396                 fprintf(stderr, "nsizescale=%f,iterations=%d\n",
00397                         nsizeScale, iterations);
00398             iterations = 0;
00399             converged = FALSE;
00400         }
00401 
00402 
00403         /* now we find the optimizer of trace(X'LX)+X'B by solving 'dim' 
00404          * system of equations, thereby obtaining the new coordinates.
00405          * If we use the constraints (given by the var's: 'ordering', 
00406          * 'levels' and 'num_levels'), we cannot optimize 
00407          * trace(X'LX)+X'B by simply solving equations, but we have
00408          * to use a quadratic programming solver
00409          * note: 'lap2' is a packed symmetric matrix, that is its 
00410          * upper-triangular part is arranged in a vector row-wise
00411          * also note: 'lap2' is really the negated laplacian (the 
00412          * laplacian is -'lap2')
00413          */
00414 
00415         if (opt->noverlap == 1 && nsizeScale > 0.001) {
00416             generateNonoverlapConstraints(cMajEnvHor, nsizeScale, coords,
00417                                           0,
00418                                           nsizeScale < 0.5 ? FALSE : TRUE,
00419                                           opt);
00420         }
00421         if (cMajEnvHor->m > 0) {
00422 #ifdef MOSEK
00423             if (opt->mosek) {
00424                 mosek_quad_solve_sep(cMajEnvHor->mosekEnv, n, b[0],
00425                                      coords[0]);
00426             } else
00427 #endif                          /* MOSEK */
00428                 constrained_majorization_vpsc(cMajEnvHor, b[0], coords[0],
00429                                               localConstrMajorIterations);
00430         } else {
00431             /* if there are no constraints then use conjugate gradient
00432              * optimisation which should be considerably faster
00433              */
00434             if (conjugate_gradient_mkernel(lap2, coords[0], b[0], n,
00435                                        tolerance_cg, n) < 0) {
00436                 iterations = -1;
00437                 goto finish;
00438             }
00439         }
00440         if (opt->noverlap == 1 && nsizeScale > 0.001) {
00441             generateNonoverlapConstraints(cMajEnvVrt, nsizeScale, coords,
00442                                           1, FALSE, opt);
00443         }
00444         if (cMajEnvVrt->m > 0) {
00445 #ifdef MOSEK
00446             if (opt->mosek) {
00447                 mosek_quad_solve_sep(cMajEnvVrt->mosekEnv, n, b[1],
00448                                      coords[1]);
00449             } else
00450 #endif                          /* MOSEK */
00451                 if (constrained_majorization_vpsc(cMajEnvVrt, b[1], coords[1],
00452                                               localConstrMajorIterations) < 0) {
00453                 iterations = -1;
00454                 goto finish;
00455             }
00456         } else {
00457             conjugate_gradient_mkernel(lap2, coords[1], b[1], n,
00458                                        tolerance_cg, n);
00459         }
00460     }
00461     if (Verbose) {
00462         fprintf(stderr, "\nfinal e = %f %d iterations %.2f sec\n",
00463                 new_stress, iterations, elapsed_sec());
00464     }
00465     deleteCMajEnvVPSC(cMajEnvHor);
00466     deleteCMajEnvVPSC(cMajEnvVrt);
00467 
00468     if (opt->noverlap == 2) {
00469         /* fprintf(stderr, "Removing overlaps as post-process...\n"); */
00470         removeoverlaps(orig_n, coords, opt);
00471     }
00472 
00473 finish:
00474     if (coords != NULL) {
00475         for (i = 0; i < dim; i++) {
00476             for (j = 0; j < orig_n; j++) {
00477                 d_coords[i][j] = coords[i][j];
00478             }
00479         }
00480         free(coords[0]);
00481         free(coords);
00482     }
00483 
00484     if (b) {
00485         free(b[0]);
00486         free(b);
00487     }
00488     free(tmp_coords);
00489     free(dist_accumulator);
00490     free(degrees);
00491     free(lap2);
00492     free(lap1);
00493 
00494     return iterations;
00495 }
00496 #endif                          /* IPSEPCOLA */