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

Go to the documentation of this file.
00001 /* $Id: constrained_majorization.c,v 1.5 2006/12/07 22:49:36 erg Exp $ $Revision: 1.5 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 #include "digcola.h"
00018 #ifdef DIGCOLA
00019 #include <math.h>
00020 #include <stdlib.h>
00021 #include <time.h>
00022 #include <stdio.h>
00023 #include <float.h>
00024 #include "stress.h"
00025 #include "dijkstra.h"
00026 #include "bfs.h"
00027 #include "matrix_ops.h"
00028 #include "kkutils.h"
00029 #include "conjgrad.h"
00030 #include "quad_prog_solver.h"
00031 #include "matrix_ops.h"
00032 
00033 #define localConstrMajorIterations 15
00034 #define levels_sep_tol 1e-1
00035 
00036 int 
00037 stress_majorization_with_hierarchy(
00038     vtx_data* graph,    /* Input graph in sparse representation  */
00039     int n,              /* Number of nodes */
00040     int nedges_graph,   /* Number of edges */
00041     double** d_coords,  /* Coordinates of nodes (output layout)  */
00042     int dim,            /* Dimemsionality of layout */
00043     int smart_ini,      /* smart initialization */
00044     int model,          /* difference model */
00045     int maxi,           /* max iterations */
00046     double levels_gap
00047 )
00048 {
00049     int iterations = 0;    /* Output: number of iteration of the process */
00050 
00051         /*************************************************
00052         ** Computation of full, dense, unrestricted k-D ** 
00053         ** stress minimization by majorization          ** 
00054         ** This function imposes HIERARCHY CONSTRAINTS  **
00055         *************************************************/
00056 
00057         int i,j,k;
00058         boolean directionalityExist = FALSE;
00059         float * lap1 = NULL;
00060         float * dist_accumulator = NULL;
00061         float * tmp_coords = NULL;
00062         float ** b = NULL;
00063 #ifdef NONCORE
00064         FILE * fp=NULL;
00065 #endif
00066         double * degrees = NULL;
00067         float * lap2=NULL;
00068         int lap_length;
00069         float * f_storage=NULL;
00070         float ** coords=NULL;
00071 
00072         double conj_tol=tolerance_cg;        /* tolerance of Conjugate Gradient */
00073         float ** unpackedLap = NULL;
00074         CMajEnv *cMajEnv = NULL;
00075         double y_0;
00076         int length;
00077         DistType diameter;
00078         float * Dij=NULL;
00079     /* to compensate noises, we never consider gaps smaller than 'abs_tol' */
00080         double abs_tol=1e-2; 
00081     /* Additionally, we never consider gaps smaller than 'abs_tol'*<avg_gap> */
00082     double relative_tol=levels_sep_tol; 
00083         int *ordering=NULL, *levels=NULL;
00084         float constant_term;
00085         int count;
00086         double degree;
00087         int step;
00088         float val;
00089         double old_stress, new_stress;
00090         boolean converged;
00091         int len;
00092     int num_levels;
00093     float *hierarchy_boundaries;
00094 
00095         if (graph[0].edists!=NULL) {
00096                 for (i=0; i<n; i++) {
00097                         for (j=1; j<graph[i].nedges; j++) {
00098                                  directionalityExist = directionalityExist || (graph[i].edists[j]!=0);
00099                         }
00100                 }
00101         }
00102         if (!directionalityExist) {
00103                 return stress_majorization_kD_mkernel(graph, n, nedges_graph, d_coords, dim, smart_ini, model, maxi);
00104         }
00105 
00106         /******************************************************************
00107         ** First, partition nodes into layers: These are our constraints **
00108         ******************************************************************/
00109 
00110         if (smart_ini) {
00111                 double* x;
00112                 double* y;
00113                 if (dim>2) {
00114                         /* the dim==2 case is handled below                      */
00115                         stress_majorization_kD_mkernel(graph, n, nedges_graph, d_coords+1, dim-1, smart_ini, model, 15);
00116                         /* now copy the y-axis into the (dim-1)-axis */
00117                         for (i=0; i<n; i++) {
00118                                 d_coords[dim-1][i] = d_coords[1][i];
00119                         }
00120                 }
00121 
00122                 x = d_coords[0]; y = d_coords[1];
00123                 compute_y_coords(graph, n, y, n);
00124                 compute_hierarchy(graph, n, abs_tol, relative_tol, y, &ordering, &levels, &num_levels);
00125                 if (num_levels<=1) {
00126                         /* no hierarchy found, use faster algorithm */
00127                         return stress_majorization_kD_mkernel(graph, n, nedges_graph, d_coords, dim, smart_ini, model, maxi);
00128                 }
00129 
00130                 if (levels_gap>0) {
00131                         /* ensure that levels are separated in the initial layout */
00132                         double displacement = 0;
00133                         int stop;
00134                         for (i=0; i<num_levels; i++) {
00135                                 displacement+=MAX((double)0,levels_gap-(y[ordering[levels[i]]]+displacement-y[ordering[levels[i]-1]]));
00136                                 stop = i<num_levels-1 ? levels[i+1] : n;
00137                                 for (j=levels[i]; j<stop; j++) {
00138                                         y[ordering[j]] += displacement;
00139                                 }
00140                         }
00141                 }
00142                 if (dim==2) {
00143                         IMDS_given_dim(graph, n, y, x, Epsilon);
00144                 }
00145         }
00146         else {
00147         initLayout(graph, n, dim, d_coords);
00148                 compute_hierarchy(graph, n, abs_tol, relative_tol, NULL, &ordering, &levels, &num_levels);              
00149         }
00150     if (n == 1) return 0;
00151 
00152         hierarchy_boundaries = N_GNEW(num_levels, float);
00153 
00154         /****************************************************
00155         ** Compute the all-pairs-shortest-distances matrix **
00156         ****************************************************/
00157 
00158         if (maxi==0)
00159                 return iterations;
00160 
00161     if (model == MODEL_SUBSET) {
00162         /* weight graph to separate high-degree nodes */
00163         /* and perform slower Dijkstra-based computation */
00164         if (Verbose)
00165             fprintf(stderr, "Calculating subset model");
00166         Dij = compute_apsp_artifical_weights_packed(graph, n);
00167     } else if (model == MODEL_CIRCUIT) {
00168         Dij = circuitModel(graph, n);
00169         if (!Dij) {
00170             agerr(AGWARN,
00171                   "graph is disconnected. Hence, the circuit model\n");
00172             agerr(AGPREV,
00173                   "is undefined. Reverting to the shortest path model.\n");
00174         }
00175     }
00176     if (!Dij) {
00177         if (Verbose)
00178             fprintf(stderr, "Calculating shortest paths");
00179         Dij = compute_apsp_packed(graph, n);
00180     }
00181 
00182         diameter=-1;
00183         length = n+n*(n-1)/2;
00184         for (i=0; i<length; i++) {
00185                 if (Dij[i]>diameter) {
00186                         diameter = (int)Dij[i];
00187                 }
00188         }
00189 
00190         if (!smart_ini) {
00191                 /* for numerical stability, scale down layout            */
00192                 /* No Jiggling, might conflict with constraints                  */
00193                 double max=1;           
00194                 for (i=0; i<dim; i++) { 
00195                         for (j=0; j<n; j++) {
00196                                 if (fabs(d_coords[i][j])>max) {
00197                                         max=fabs(d_coords[i][j]);
00198                                 }
00199                         }       
00200                 }
00201                 for (i=0; i<dim; i++) { 
00202                         for (j=0; j<n; j++) {
00203                                 d_coords[i][j]*=10/max;
00204                         }       
00205                 }
00206         }               
00207 
00208         if (levels_gap>0) {
00209                 int length = n+n*(n-1)/2;
00210                 double sum1, sum2, scale_ratio;
00211                 int count;
00212                 sum1=(float)(n*(n-1)/2);
00213                 sum2=0;
00214                 for (count=0, i=0; i<n-1; i++) {
00215                         count++; // skip self distance
00216                         for (j=i+1; j<n; j++,count++) {
00217                                 sum2+=distance_kD(d_coords, dim, i, j)/Dij[count];
00218                         }
00219                 }
00220                 scale_ratio=sum2/sum1;
00221                 /* double scale_ratio=10; */
00222                 for (i=0; i<length; i++) {
00223                         Dij[i]*=(float)scale_ratio;
00224                 }
00225         }
00226 
00227         /**************************
00228         ** Layout initialization **
00229         **************************/
00230 
00231         for (i=0; i<dim; i++) {         
00232                 orthog1(n, d_coords[i]);
00233         }
00234 
00235         /* for the y-coords, don't center them, but translate them so y[0]=0 */
00236         y_0 = d_coords[1][0];
00237         for (i=0; i<n; i++) {
00238                 d_coords[1][i] -= y_0;
00239         }
00240 
00241         coords = N_GNEW(dim, float*);
00242         f_storage = N_GNEW(dim*n, float);
00243         for (i=0; i<dim; i++) {
00244                 coords[i] = f_storage+i*n;
00245                 for (j=0; j<n; j++) {
00246                         coords[i][j] = (float)(d_coords[i][j]);
00247                 }
00248         }
00249 
00250         /* compute constant term in stress sum
00251          * which is \sum_{i<j} w_{ij}d_{ij}^2
00252      */
00253         constant_term=(float)(n*(n-1)/2);
00254         
00255         /**************************
00256         ** Laplacian computation **
00257         **************************/
00258                         
00259         lap2 = Dij;
00260         lap_length = n+n*(n-1)/2;
00261         square_vec(lap_length, lap2);
00262         /* compute off-diagonal entries */
00263         invert_vec(lap_length, lap2);
00264         
00265         /* compute diagonal entries */
00266         count=0;
00267         degrees = N_GNEW(n, double);
00268         set_vector_val(n, 0, degrees);
00269         for (i=0; i<n-1; i++) {
00270                 degree=0;
00271                 count++; // skip main diag entry
00272                 for (j=1; j<n-i; j++,count++) {
00273                         val = lap2[count];
00274                         degree+=val; degrees[i+j]-=val;
00275                 }
00276                 degrees[i]-=degree;
00277         }
00278         for (step=n,count=0,i=0; i<n; i++,count+=step,step--) {
00279                 lap2[count]=(float)degrees[i];
00280         }
00281 
00282 #ifdef NONCORE
00283         fpos_t pos;
00284         if (n>max_nodes_in_mem) {
00285                 #define FILENAME "tmp_Dij$$$.bin"
00286                 fp = fopen(FILENAME, "wb");
00287                 fwrite(lap2, sizeof(float), lap_length, fp);
00288                 fclose(fp);
00289         }
00290 #endif
00291                 
00292         /*************************
00293         ** Layout optimization  **
00294         *************************/
00295         
00296         b = N_GNEW (dim, float*);
00297         b[0] = N_GNEW (dim*n, float);
00298         for (k=1; k<dim; k++) {
00299                 b[k] = b[0]+k*n;
00300         }
00301 
00302         tmp_coords = N_GNEW(n, float);
00303         dist_accumulator = N_GNEW(n, float);
00304 #ifdef NONCORE
00305         if (n<=max_nodes_in_mem) {
00306 #endif
00307                 lap1 = N_GNEW(lap_length, float);
00308 #ifdef NONCORE
00309         }
00310         else {
00311                 lap1=lap2;
00312                 fp = fopen(FILENAME, "rb");
00313                 fgetpos(fp, &pos);
00314         }
00315 #endif
00316         
00317         old_stress=DBL_MAX; /* at least one iteration */
00318 
00319         unpackedLap = unpackMatrix(lap2, n);
00320         cMajEnv=initConstrainedMajorization(lap2, n, ordering, levels, num_levels);
00321 
00322         for (converged=FALSE,iterations=0; iterations<maxi && !converged; iterations++) {
00323 
00324                 /* First, construct Laplacian of 1/(d_ij*|p_i-p_j|)  */
00325                 set_vector_val(n, 0, degrees);
00326 #ifdef NONCORE
00327                 if (n<=max_nodes_in_mem) {
00328 #endif
00329                         sqrt_vecf(lap_length, lap2, lap1);
00330 #ifdef NONCORE
00331                 }
00332                 else {
00333                         sqrt_vec(lap_length, lap1);
00334                 }
00335 #endif
00336                 for (count=0,i=0; i<n-1; i++) {
00337                         len = n-i-1;
00338                         /* init 'dist_accumulator' with zeros */
00339                         set_vector_valf(n, 0, dist_accumulator);
00340                         
00341                         /* put into 'dist_accumulator' all squared distances 
00342              * between 'i' and 'i'+1,...,'n'-1
00343              */
00344                         for (k=0; k<dim; k++) { 
00345                                 set_vector_valf(len, coords[k][i], tmp_coords);
00346                                 vectors_mult_additionf(len, tmp_coords, -1, coords[k]+i+1);
00347                                 square_vec(len, tmp_coords);
00348                                 vectors_additionf(len, tmp_coords, dist_accumulator, dist_accumulator);
00349                         }                       
00350                 
00351                         /* convert to 1/d_{ij} */
00352                         invert_sqrt_vec(len, dist_accumulator);
00353                         /* detect overflows */
00354                         for (j=0; j<len; j++) {
00355                                 if (dist_accumulator[j]>=FLT_MAX || dist_accumulator[j]<0) {
00356                                         dist_accumulator[j]=0;
00357                                 }
00358                         }
00359                         
00360                         count++; /* save place for the main diagonal entry */
00361                         degree=0;
00362                         for (j=0; j<len; j++,count++) {
00363                                 val=lap1[count]*=dist_accumulator[j];
00364                                 degree+=val; degrees[i+j+1]-=val;
00365                         }
00366                         degrees[i]-=degree;                     
00367                 }
00368                 for (step=n,count=0,i=0; i<n; i++,count+=step,step--) {
00369                         lap1[count]=(float)degrees[i];
00370                 }
00371 
00372                 /* Now compute b[] (L^(X(t))*X(t)) */
00373                 for (k=0; k<dim; k++) { 
00374                         /* b[k] := lap1*coords[k] */
00375                         right_mult_with_vector_ff(lap1, n, coords[k], b[k]);
00376                 }
00377                 
00378                 /* compute new stress
00379                  * remember that the Laplacians are negated, so we subtract 
00380          * instead of add and vice versa
00381          */
00382                 new_stress=0;
00383                 for (k=0; k<dim; k++) { 
00384                         new_stress+=vectors_inner_productf(n, coords[k], b[k]);
00385                 }
00386                 new_stress*=2;
00387                 new_stress+=constant_term; // only after mult by 2              
00388 #ifdef NONCORE
00389                 if (n>max_nodes_in_mem) {
00390                         /* restore lap2 from disk */
00391                         fsetpos(fp, &pos);
00392                         fread(lap2, sizeof(float), lap_length, fp);
00393                 }
00394 #endif
00395                 for (k=0; k<dim; k++) { 
00396                         right_mult_with_vector_ff(lap2, n, coords[k], tmp_coords);
00397                         new_stress-=vectors_inner_productf(n, coords[k], tmp_coords);
00398                 }
00399 
00400 #ifdef ALTERNATIVE_STRESS_CALC
00401                 {
00402                 double mat_stress=new_stress;
00403                 double compute_stress(float ** coords, float * lap, int dim, int n);
00404                 new_stress = compute_stress(coords, lap2, dim, n);
00405                 if (fabs(mat_stress-new_stress)/min(mat_stress,new_stress)>0.001) {
00406                         fprintf(stderr,"Diff stress vals: %lf %lf (iteration #%d)\n", mat_stress, new_stress,iterations);
00407                 }
00408                 }
00409 #endif
00410                 /* check for convergence */
00411                 converged = fabs(new_stress-old_stress)/fabs(old_stress+1e-10) < Epsilon;
00412                 converged = converged || (iterations>1 && new_stress>old_stress); 
00413                         /* in first iteration we allowed stress increase, which 
00414              * might result ny imposing constraints
00415              */
00416                 old_stress = new_stress;
00417                 
00418                 for (k=0; k<dim; k++) {
00419                         /* now we find the optimizer of trace(X'LX)+X'B by solving 'dim' 
00420              * system of equations, thereby obtaining the new coordinates.
00421                          * If we use the constraints (given by the var's: 'ordering', 
00422              * 'levels' and 'num_levels'), we cannot optimize 
00423              * trace(X'LX)+X'B by simply solving equations, but we have
00424                          * to use a quadratic programming solver
00425                          * note: 'lap2' is a packed symmetric matrix, that is its 
00426              * upper-triangular part is arranged in a vector row-wise
00427                          * also note: 'lap2' is really the negated laplacian (the 
00428              * laplacian is -'lap2')
00429              */
00430                         
00431                         if (k==1) {
00432                                 /* use quad solver in the y-dimension */
00433                                 constrained_majorization_new_with_gaps(cMajEnv, b[k], coords, dim, k, localConstrMajorIterations, hierarchy_boundaries, levels_gap);
00434         
00435                         }
00436                         else {
00437                                 /* use conjugate gradient for all dimensions except y */
00438                                 conjugate_gradient_mkernel(lap2, coords[k], b[k], n, conj_tol, n);      
00439                         }
00440                 }
00441         }
00442         free (hierarchy_boundaries);
00443         deleteCMajEnv(cMajEnv);
00444         
00445         if (coords!=NULL) {
00446                 for (i=0; i<dim; i++) {
00447                         for (j=0; j<n; j++) {
00448                                 d_coords[i][j] = coords[i][j];
00449                         }
00450                 }
00451                 free (coords[0]); free (coords);
00452         }
00453         
00454         if (b) {        
00455                 free (b[0]); free (b);
00456         }
00457         free (tmp_coords);
00458         free (dist_accumulator);
00459         free (degrees);
00460         free (lap2);
00461         
00462 
00463 #ifdef NONCORE
00464         if (n<=max_nodes_in_mem) {
00465 #endif
00466                 free (lap1); 
00467 #ifdef NONCORE
00468         }
00469 #endif
00470 
00471         free (ordering); 
00472         
00473         free (levels);
00474 
00475         if (unpackedLap) {
00476                 free (unpackedLap[0]); free (unpackedLap);
00477         }
00478     return iterations;
00479 }
00480 #endif /* DIGCOLA */
00481 

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