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

Go to the documentation of this file.
00001 /* $Id: stress.c,v 1.7 2006/12/07 22:49:37 erg Exp $ $Revision: 1.7 $ */
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 
00018 #include "neato.h"
00019 #include "dijkstra.h"
00020 #include "bfs.h"
00021 #include "pca.h"
00022 #include "matrix_ops.h"
00023 #include "conjgrad.h"
00024 #include "embed_graph.h"
00025 #include "kkutils.h"
00026 #include "stress.h"
00027 #include <math.h>
00028 #include <stdlib.h>
00029 #include <time.h>
00030 
00031 #ifdef UNUSED
00032  /* Full dense stress optimization (equivalent to Kamada-Kawai's energy) */
00033  /* Slowest and most accurate optimization */
00034 static int stress_majorization_kD(vtx_data * graph,     /* Input graph in sparse representation */
00035                                   int n,        /* Number of nodes */
00036                                   int nedges_graph,     /* Number of edges */
00037                                   double **coords,      /* coordinates of nodes (output layout) */
00038                                   int dim,      /* dimemsionality of layout */
00039                                   int smart_ini,        /* smart initialization */
00040                                   int reweight_graph,   /* difference model */
00041                                   int maxi      /* max iterations */
00042     );
00043 
00044  /* Optimization of the stress function using sparse distance matrix */
00045  /* Faster than dense method, but less accurate */
00046 static int sparse_stress_majorization_kD(vtx_data * graph,      /* Input graph in sparse representation */
00047                                          int n, /* Number of nodes */
00048                                          int nedges_graph,      /* Number of edges */
00049                                          double **coords,       /* coordinates of nodes (output layout)  */
00050                                          int dim,       /* dimemsionality of layout */
00051                                          int smart_ini, /* smart initialization */
00052                                          int reweight_graph,    /* difference model */
00053                                          int maxi,      /* max iterations */
00054                                          int dist_bound,        /* neighborhood in sparse distance matrix */
00055                                          int num_centers        /* #pivots in sparse distance matrix */
00056     );
00057 #endif
00058 
00059  /* Optimization of the stress function using sparse distance matrix, within a vector-space */
00060  /* Fastest and least accurate method */
00061 static int sparse_stress_subspace_majorization_kD(vtx_data * graph,     /* Input graph in sparse representation */
00062                                                   int n,        /* Number of nodes */
00063                                                   int nedges_graph,     /* Number of edges */
00064                                                   double **coords,      /* coordinates of nodes (output layout)  */
00065                                                   int dim,      /* dimemsionality of layout */
00066                                                   int smart_ini,        /* smart initialization */
00067                                                   int reweight_graph,   /* difference model */
00068                                                   int maxi,     /* max iterations */
00069                                                   int dist_bound,       /* neighborhood in sparse distance matrix */
00070                                                   int num_centers       /* #pivots in sparse distance matrix */
00071     );
00072 
00073 #ifndef HAVE_DRAND48
00074 extern double drand48(void);
00075 #endif
00076 
00077 #define Dij2                    /* If defined, the terms in the stress energy are normalized 
00078                                    by d_{ij}^{-2} otherwise, they are normalized by d_{ij}^{-1}
00079                                  */
00080 
00081 #ifdef NONCORE
00082 /* Set 'max_nodes_in_mem' so that 
00083  * 4*(max_nodes_in_mem^2) is smaller than the available memory (in bytes)
00084  * 4 = sizeof(float)
00085  */
00086 #define max_nodes_in_mem 18000
00087 #endif
00088 
00089  /* relevant when using sparse distance matrix not within subspace */
00090 #define smooth_pivots true
00091 
00092 /* dimensionality of subspace; relevant 
00093  * when optimizing within subspace) 
00094  */
00095 #define stress_pca_dim 50
00096 
00097  /* a structure used for storing sparse distance matrix */
00098 typedef struct {
00099     int nedges;
00100     int *edges;
00101     DistType *edist;
00102     boolean free_mem;
00103 } dist_data;
00104 
00105 static double compute_stressf(float **coords, float *lap, int dim, int n)
00106 {
00107     /* compute the overall stress */
00108 
00109     int i, j, l, neighbor, count;
00110     double sum, dist, Dij;
00111     sum = 0;
00112     for (count = 0, i = 0; i < n - 1; i++) {
00113         count++;                /* skip diagonal entry */
00114         for (j = 1; j < n - i; j++, count++) {
00115             dist = 0;
00116             neighbor = i + j;
00117             for (l = 0; l < dim; l++) {
00118                 dist +=
00119                     (coords[l][i] - coords[l][neighbor]) * (coords[l][i] -
00120                                                             coords[l]
00121                                                             [neighbor]);
00122             }
00123             dist = sqrt(dist);
00124 #ifdef Dij2
00125             Dij = 1.0 / sqrt(lap[count]);
00126             sum += (Dij - dist) * (Dij - dist) * (lap[count]);
00127 #else
00128             Dij = 1.0 / lap[count];
00129             sum += (Dij - dist) * (Dij - dist) * (lap[count]);
00130 #endif
00131         }
00132     }
00133 
00134     return sum;
00135 }
00136 
00137 #ifdef UNUSED
00138 static double compute_stress(double **coords, int **Dij, int dim, int n)
00139 {
00140     /* compute the overall stress */
00141 
00142     int i, j, l;
00143     double sum, dist;
00144     sum = 0;
00145     for (i = 1; i < n; i++) {
00146         for (j = 0; j < i; j++) {
00147             dist = 0;
00148             for (l = 0; l < dim; l++) {
00149                 dist +=
00150                     (coords[l][i] - coords[l][j]) * (coords[l][i] -
00151                                                      coords[l][j]);
00152             }
00153             dist = sqrt(dist);
00154 #ifdef Dij2
00155             sum +=
00156                 (Dij[i][j] - dist) * (Dij[i][j] -
00157                                       dist) / (Dij[i][j] * Dij[i][j]);
00158 #else
00159             sum += (Dij[i][j] - dist) * (Dij[i][j] - dist) / Dij[i][j];
00160 #endif
00161         }
00162     }
00163 
00164     return sum;
00165 }
00166 #endif
00167 
00168 static double
00169 compute_stress1(double **coords, dist_data * distances, int dim, int n)
00170 {
00171     /* compute the overall stress */
00172 
00173     int i, j, l, node;
00174     double sum, dist, Dij;
00175     sum = 0;
00176     for (i = 0; i < n; i++) {
00177         for (j = 0; j < distances[i].nedges; j++) {
00178             node = distances[i].edges[j];
00179             if (node <= i) {
00180                 continue;
00181             }
00182             dist = 0;
00183             for (l = 0; l < dim; l++) {
00184                 dist +=
00185                     (coords[l][i] - coords[l][node]) * (coords[l][i] -
00186                                                         coords[l][node]);
00187             }
00188             dist = sqrt(dist);
00189             Dij = distances[i].edist[j];
00190 #ifdef Dij2
00191             sum += (Dij - dist) * (Dij - dist) / (Dij * Dij);
00192 #else
00193             sum += (Dij - dist) * (Dij - dist) / Dij;
00194 #endif
00195         }
00196     }
00197 
00198     return sum;
00199 }
00200 
00201 /* initLayout:
00202  * Initialize node coordinates. If the node already has
00203  * a position, use it.
00204  * Return true if some node is fixed.
00205  */
00206 int
00207 initLayout(vtx_data * graph, int n, int dim, double **coords)
00208 {
00209     node_t *np;
00210     double *xp;
00211     double *yp;
00212     double *pt;
00213     int i, d;
00214     int pinned = 0;
00215 
00216     xp = coords[0];
00217     yp = coords[1];
00218     for (i = 0; i < n; i++) {
00219         np = graph[i].np;
00220         if (hasPos(np)) {
00221             pt = ND_pos(np);
00222             *xp++ = *pt++;
00223             *yp++ = *pt++;
00224             if (dim > 2) {
00225                 for (d = 2; d < dim; d++)
00226                     coords[d][i] = *pt++;
00227             }
00228             if (isFixed(np))
00229                 pinned = 1;
00230         } else {
00231             *xp++ = drand48();
00232             *yp++ = drand48();
00233             if (dim > 2) {
00234                 for (d = 2; d < dim; d++)
00235                     coords[d][i] = drand48();
00236             }
00237         }
00238     }
00239 
00240     for (d = 0; d < dim; d++)
00241         orthog1(n, coords[d]);
00242 
00243     return pinned;
00244 }
00245 
00246 float*
00247 circuitModel(vtx_data * graph, int nG)
00248 {
00249     int i, j, e, rv, count;
00250     float *Dij = N_NEW(nG * (nG + 1) / 2, float);
00251     double **Gm;
00252     double **Gm_inv;
00253 
00254     Gm = new_array(nG, nG, 0.0);
00255     Gm_inv = new_array(nG, nG, 0.0);
00256 
00257     /* set non-diagonal entries */
00258     if (graph->ewgts) {
00259         for (i = 0; i < nG; i++) {
00260             for (e = 1; e < graph[i].nedges; e++) {
00261                 j = graph[i].edges[e];
00262                 /* conductance is 1/resistance */
00263                 Gm[i][j] = Gm[j][i] = -1.0 / graph[i].ewgts[e]; /* negate */
00264             }
00265         }
00266     } else {
00267         for (i = 0; i < nG; i++) {
00268             for (e = 1; e < graph[i].nedges; e++) {
00269                 j = graph[i].edges[e];
00270                 /* conductance is 1/resistance */
00271                 Gm[i][j] = Gm[j][i] = -1.0;     /* ewgts are all 1 */
00272             }
00273         }
00274     }
00275 
00276     rv = solveCircuit(nG, Gm, Gm_inv);
00277 
00278     if (rv) {
00279         float v;
00280         count = 0;
00281         for (i = 0; i < nG; i++) {
00282             for (j = i; j < nG; j++) {
00283                 if (i == j)
00284                     v = 0.0;
00285                 else
00286                     v = (float) (Gm_inv[i][i] + Gm_inv[j][j] -
00287                                  2.0 * Gm_inv[i][j]);
00288                 Dij[count++] = v;
00289             }
00290         }
00291     } else {
00292         free(Dij);
00293         Dij = NULL;
00294     }
00295     free_array(Gm);
00296     free_array(Gm_inv);
00297     return Dij;
00298 }
00299 
00300 #ifdef UNUSED
00301 int stress_majorization_kD(vtx_data * graph,    /* Input graph in sparse representation */
00302                            int n,       /* Number of nodes */
00303                            int nedges_graph,    /* Number of edges */
00304                            double **coords,     /* coordinates of nodes(output layout)  */
00305                            int dim,     /* dimemsionality of layout */
00306                            int smart_ini,       /* smart initialization */
00307                            int reweight_graph,  /* difference model */
00308                            int n_iterations     /* max #iterations */
00309     )
00310 {
00311     int iterations;             /* output: number of iteration of the process */
00312     double conj_tol = tolerance_cg;     /* tolerance of Conjugate Gradient */
00313     DistType **Dij;
00314     float *f_storage;
00315     float **lap;
00316     double degree;
00317     double dist_ij;
00318     double *b;
00319     double L_ij;
00320     double old_stress, new_stress;
00321     boolean converged;
00322 
00323         /*************************************************
00324         ** Computation of full, dense, unrestricted k-D ** 
00325         ** stress minimization by majorization          **    
00326         *************************************************/
00327 
00328     int i, j, k;
00329 
00330         /*************************************************
00331         * Compute the all-pairs-shortest-distances matrix *
00332         *************************************************/
00333 
00334     if (!reweight_graph) {
00335         /* unweighted computation using BFS */
00336         Dij = compute_apsp(graph, n);
00337     } else {
00338         /* weight graph to separate high-degree nodes */
00339         /* and perform slower Dijkstra-based computation */
00340         Dij = compute_apsp_artifical_weights(graph, n);
00341     }
00342 
00343         /*************************************************
00344         ** Layout initialization **
00345         *************************************************/
00346 
00347     if (smart_ini) {
00348         /* optimize layout quickly within subspace */
00349         sparse_stress_subspace_majorization_kD(graph, n, nedges_graph,
00350                                                coords, dim, smart_ini,
00351                                                reweight_graph, 50,
00352                                                neighborhood_radius_subspace,
00353                                                num_pivots_stress);
00354     } else {
00355         initLayout(graph, n, dim, coords);
00356     }
00357 
00358         /*************************************************
00359         ** Laplacian computation **
00360         *************************************************/
00361 
00362     lap = N_GNEW(n, float *);
00363     f_storage = N_GNEW(n * n, float);
00364     for (i = 0; i < n; i++) {
00365         lap[i] = f_storage + i * n;
00366         degree = 0;
00367         for (j = 0; j < n; j++) {
00368             if (j == i)
00369                 continue;
00370 #ifdef Dij2
00371             degree -= lap[i][j] = -1.0f / ((float) Dij[i][j] * (float) Dij[i][j]);      /* cast Dij to float to prevent overflow */
00372 #else
00373             degree -= lap[i][j] = -1.0f / Dij[i][j];
00374 #endif
00375         }
00376         lap[i][i] = (float) (degree);
00377     }
00378 
00379         /*************************************************
00380          Layout optimization
00381         *************************************************/
00382 
00383     b = N_GNEW(n, double);
00384     old_stress = compute_stress(coords, Dij, dim, n);
00385     for (converged = FALSE, iterations = 0;
00386          iterations < n_iterations && !converged; iterations++) {
00387 
00388         /* Axis-by-axis optimization: */
00389         for (k = 0; k < dim; k++) {
00390             /* compute the vector b */
00391             /* multiply on-the-fly with distance-based laplacian */
00392             /* (for saving storage we don't construct this Laplacian explicitly) */
00393             for (i = 0; i < n; i++) {
00394                 degree = 0;
00395                 b[i] = 0;
00396                 for (j = 0; j < n; j++) {
00397                     if (j == i)
00398                         continue;
00399                     dist_ij = distance_kD(coords, dim, i, j);
00400                     if (dist_ij > 1e-30) {      /* skip zero distances */
00401                         /* calculate L_ij := w_{ij}*d_{ij}/dist_{ij} */
00402 #ifdef Dij2
00403                         L_ij = (float) (-1 / (dist_ij * Dij[i][j]));
00404 #else
00405                         L_ij = (float) (-1 / dist_ij);
00406 #endif
00407                         degree -= L_ij;
00408                         b[i] += L_ij * coords[k][j];
00409                     }
00410                 }
00411                 b[i] += degree * coords[k][i];
00412             }
00413             conjugate_gradient_f(lap, coords[k], b, n, conj_tol, n, TRUE);
00414         }
00415 
00416         if ((converged = (iterations % 2 == 0))) {      /* check for convergence every two iterations */
00417             new_stress = compute_stress(coords, Dij, dim, n);
00418             converged =
00419                 fabs(new_stress - old_stress) / (new_stress + 1e-10) <
00420                 Epsilon;
00421             old_stress = new_stress;
00422             if (Verbose && (iterations % 10 == 0)) {
00423                 fprintf(stderr, "%.3f ", new_stress);
00424                 if (iterations % 100 == 0)
00425                     fprintf(stderr, "\n");
00426             }
00427         }
00428     }
00429     if (Verbose)
00430         fprintf(stderr, "\nfinal e = %f\n",
00431                 compute_stress(coords, Dij, dim, n));
00432 
00433 
00434     free(Dij[0]);
00435     free(Dij);
00436     free(lap[0]);
00437     free(lap);
00438     free(b);
00439 
00440     return (iterations);
00441 }
00442 
00443 static void
00444 local_beautify_kD(int *nodes, int num_nodes, vtx_data * graph, int n,
00445                   int dist_bound, int reweight_graph, double **coords,
00446                   int dim)
00447 {
00448     /* Optimize locally the k-D position of each of the nodes in 'nodes' */
00449     /* sing majorization.  */
00450     /* Here, in each iteration only a single node is mobile */
00451 
00452     int i, j, k;
00453     int *visited_nodes;
00454     DistType *dist;
00455     double *weights;
00456     Queue Q;
00457     int num_visited_nodes;
00458     double dist_ij;
00459     int v, neighbor;
00460     double dist_1d;
00461     double total_wgts;
00462     double *newpos;
00463     double max_diff;
00464 
00465     if (dist_bound <= 0) {
00466         return;
00467     }
00468 
00469     visited_nodes = N_GNEW(n, int);
00470     dist = N_GNEW(n, DistType);
00471     weights = N_GNEW(n, double);
00472     newpos = N_GNEW(dim, double);
00473     mkQueue(&Q, n);
00474 
00475     /* initialize dist to -1, important for bfs_bounded */
00476     for (i = 0; i < n; i++) {
00477         dist[i] = -1;
00478     }
00479 
00480     for (i = 0; i < num_nodes; i++) {
00481         v = nodes[i];
00482         if (reweight_graph) {
00483             num_visited_nodes =
00484                 dijkstra_bounded(v, graph, n, dist, dist_bound,
00485                                  visited_nodes);
00486         } else {
00487             num_visited_nodes =
00488                 bfs_bounded(v, graph, n, dist, &Q, dist_bound,
00489                             visited_nodes);
00490         }
00491 
00492         total_wgts = 0;
00493         for (j = 0; j < num_visited_nodes; j++) {
00494             neighbor = visited_nodes[j];
00495             if (neighbor != v) {
00496 #ifdef Dij2
00497                 total_wgts += weights[j] =
00498                     1.0 / ((double) dist[neighbor] *
00499                            (double) dist[neighbor]);
00500 #else
00501                 total_wgts += weights[j] = 1.0 / (double) dist[neighbor];
00502 #endif
00503             }
00504         }
00505 
00506         if (total_wgts == 0) {  /* no neighbors to current node */
00507             continue;
00508         }
00509 
00510         do {
00511             for (k = 0; k < dim; newpos[k++] = 0);
00512 
00513             for (j = 0; j < num_visited_nodes; j++) {
00514                 neighbor = visited_nodes[j];
00515                 if (neighbor == v) {
00516                     continue;
00517                 }
00518                 for (k = 0; k < dim; k++) {
00519                     dist_1d = coords[k][v] - coords[k][neighbor];
00520                     dist_ij = distance_kD(coords, dim, v, neighbor);
00521                     newpos[k] +=
00522                         weights[j] * (coords[k][neighbor] +
00523                                       dist[neighbor] * dist_1d / dist_ij);
00524                 }
00525             }
00526             max_diff = 0;
00527             for (k = 0; k < dim; k++) {
00528                 newpos[k] /= total_wgts;
00529                 max_diff =
00530                     MAX(max_diff,
00531                         fabs(newpos[k] - coords[k][v]) / fabs(newpos[k] +
00532                                                               1e-20));
00533                 coords[k][v] = newpos[k];
00534             }
00535         } while (max_diff > Epsilon);
00536 
00537         /* initialize 'dist' for next run of 'bfs_bounded' */
00538         for (j = 0; j < num_visited_nodes; j++) {
00539             dist[visited_nodes[j]] = -1;
00540         }
00541     }
00542 
00543     free(visited_nodes);
00544     free(dist);
00545     free(weights);
00546     free(newpos);
00547     freeQueue(&Q);
00548 }
00549 
00550 int sparse_stress_majorization_kD(vtx_data * graph,     /* Input graph in sparse representation */
00551                                   int n,        /* Number of nodes */
00552                                   int nedges_graph,     /* Number of edges */
00553                                   double **coords,      /* coordinates of nodes (output layout)  */
00554                                   int dim,      /* dimemsionality of layout */
00555                                   int smart_ini,        /* smart initialization */
00556                                   int reweight_graph,   /* difference model */
00557                                   int n_iterations,     /* max #iterations */
00558                                   int dist_bound,       /* neighborhood size in sparse distance matrix    */
00559                                   int num_centers       /* #pivots in sparse distance matrix  */
00560     )
00561 {
00562     int iterations;
00563     double conj_tol = tolerance_cg;     /* tolerance of Conjugate Gradient */
00564 
00565         /*************************************************
00566            Computation of pivot-based, sparse, unrestricted   
00567            k-D  stress minimization by majorization               
00568         *************************************************/
00569 
00570     int i, j, k;
00571     int node;
00572     /* if i is a pivot than CenterIndex[i] is  its index, otherwise CenterIndex[i]= -1 */
00573     int *CenterIndex;
00574     int *invCenterIndex;        /* list the pivot nodes  */
00575     Queue Q;
00576     float *old_weights;
00577     /* this matrix stores the distance  between each node and each "center" */
00578     DistType **Dij;
00579     /* this vector stores the distances of each node to the selected "centers" */
00580     DistType *dist;
00581     DistType *storage;
00582     DistType max_dist;
00583     int *visited_nodes;
00584     dist_data *distances;
00585     int available_space;
00586     int *storage1 = NULL;
00587     DistType *storage2 = NULL;
00588     int num_visited_nodes;
00589     int num_neighbors;
00590     int index;
00591     int nedges;
00592     DistType *dist_list;
00593     vtx_data *lap;
00594     int *edges;
00595     float *ewgts;
00596     double degree;
00597     double dist_ij;
00598     double *b;
00599     double L_ij;
00600     double old_stress, new_stress;
00601     boolean converged;
00602 
00603         /*************************************************
00604            Layout initialization  
00605         *************************************************/
00606 
00607     if (smart_ini) {
00608         /* optimize layout quickly within subspace */
00609         sparse_stress_subspace_majorization_kD(graph, n, nedges_graph,
00610                                                coords, dim, smart_ini,
00611                                                reweight_graph, 50,
00612                                                dist_bound, num_centers);
00613     } else {
00614         initLayout(graph, n, dim, coords);
00615     }
00616 
00617         /*************************************************
00618      Compute the sparse-shortest-distances matrix 'distances' 
00619         *************************************************/
00620 
00621     CenterIndex = N_GNEW(n, int);
00622     for (i = 0; i < n; i++) {
00623         CenterIndex[i] = -1;
00624     }
00625     invCenterIndex = NULL;
00626 
00627     mkQueue(&Q, n);
00628     old_weights = graph[0].ewgts;
00629 
00630     if (reweight_graph) {
00631         /* weight graph to separate high-degree nodes */
00632         /* in the future, perform slower Dijkstra-based computation */
00633         compute_new_weights(graph, n);
00634     }
00635 
00636     /* compute sparse distance matrix */
00637     /* first select 'num_centers' pivots from which we compute distance */
00638     /* to all other nodes */
00639 
00640     Dij = NULL;
00641     dist = N_GNEW(n, DistType);
00642 
00643     if (num_centers == 0) {     /* no pivots, skip pivots-to-nodes distance calculation */
00644         goto after_pivots_selection;
00645     }
00646 
00647     invCenterIndex = N_GNEW(num_centers, int);  /* list the pivot nodes  */
00648 
00649     storage = N_GNEW(n * num_centers, DistType);
00650     Dij = N_GNEW(num_centers, DistType *);
00651     for (i = 0; i < num_centers; i++)
00652         Dij[i] = storage + i * n;
00653 
00654     /* select 'num_centers' pivots that are uniformaly spreaded over the graph */
00655 
00656     /* the first pivots is selected randomly */
00657     node = rand() % n;
00658     CenterIndex[node] = 0;
00659     invCenterIndex[0] = node;
00660 
00661     if (reweight_graph) {
00662         dijkstra(node, graph, n, Dij[0]);
00663     } else {
00664         bfs(node, graph, n, Dij[0], &Q);
00665     }
00666 
00667     /* find the most distant node from first pivot */
00668     max_dist = 0;
00669     for (i = 0; i < n; i++) {
00670         dist[i] = Dij[0][i];
00671         if (dist[i] > max_dist) {
00672             node = i;
00673             max_dist = dist[i];
00674         }
00675     }
00676     /* select other num_centers-1 nodes as pivots */
00677     for (i = 1; i < num_centers; i++) {
00678         CenterIndex[node] = i;
00679         invCenterIndex[i] = node;
00680         if (reweight_graph) {
00681             dijkstra(node, graph, n, Dij[i]);
00682         } else {
00683             bfs(node, graph, n, Dij[i], &Q);
00684         }
00685         max_dist = 0;
00686         for (j = 0; j < n; j++) {
00687             dist[j] = MIN(dist[j], Dij[i][j]);
00688             if (dist[j] > max_dist
00689                 || (dist[j] == max_dist && rand() % (j + 1) == 0)) {
00690                 node = j;
00691                 max_dist = dist[j];
00692             }
00693         }
00694     }
00695 
00696   after_pivots_selection:
00697 
00698     /* Construct a sparse distance matrix 'distances' */
00699 
00700     /* initialize 'dist' to -1, important for 'bfs_bounded(..)' */
00701     for (i = 0; i < n; i++) {
00702         dist[i] = -1;
00703     }
00704 
00705     visited_nodes = N_GNEW(n, int);
00706     distances = N_GNEW(n, dist_data);
00707     available_space = 0;
00708     nedges = 0;
00709     for (i = 0; i < n; i++) {
00710         if (CenterIndex[i] >= 0) {      /* a pivot node */
00711             distances[i].edges = N_GNEW(n - 1, int);
00712             distances[i].edist = N_GNEW(n - 1, DistType);
00713             distances[i].nedges = n - 1;
00714             nedges += n - 1;
00715             distances[i].free_mem = TRUE;
00716             index = CenterIndex[i];
00717             for (j = 0; j < i; j++) {
00718                 distances[i].edges[j] = j;
00719                 distances[i].edist[j] = Dij[index][j];
00720             }
00721             for (j = i + 1; j < n; j++) {
00722                 distances[i].edges[j - 1] = j;
00723                 distances[i].edist[j - 1] = Dij[index][j];
00724             }
00725             continue;
00726         }
00727 
00728         /* a non pivot node */
00729 
00730         if (dist_bound > 0) {
00731             if (reweight_graph) {
00732                 num_visited_nodes =
00733                     dijkstra_bounded(i, graph, n, dist, dist_bound,
00734                                      visited_nodes);
00735             } else {
00736                 num_visited_nodes =
00737                     bfs_bounded(i, graph, n, dist, &Q, dist_bound,
00738                                 visited_nodes);
00739             }
00740             /* filter the pivots out of the visited nodes list, and the self loop: */
00741             for (j = 0; j < num_visited_nodes;) {
00742                 if (CenterIndex[visited_nodes[j]] < 0
00743                     && visited_nodes[j] != i) {
00744                     /* not a pivot or self loop */
00745                     j++;
00746                 } else {
00747                     dist[visited_nodes[j]] = -1;
00748                     visited_nodes[j] = visited_nodes[--num_visited_nodes];
00749                 }
00750             }
00751         } else {
00752             num_visited_nodes = 0;
00753         }
00754         num_neighbors = num_visited_nodes + num_centers;
00755         if (num_neighbors > available_space) {
00756             available_space = (dist_bound + 1) * n;
00757             storage1 = N_GNEW(available_space, int);
00758             storage2 = N_GNEW(available_space, DistType);
00759             distances[i].free_mem = TRUE;
00760         } else {
00761             distances[i].free_mem = FALSE;
00762         }
00763         distances[i].edges = storage1;
00764         distances[i].edist = storage2;
00765         distances[i].nedges = num_neighbors;
00766         nedges += num_neighbors;
00767         for (j = 0; j < num_visited_nodes; j++) {
00768             storage1[j] = visited_nodes[j];
00769             storage2[j] = dist[visited_nodes[j]];
00770             dist[visited_nodes[j]] = -1;
00771         }
00772         /* add all pivots: */
00773         for (j = num_visited_nodes; j < num_neighbors; j++) {
00774             index = j - num_visited_nodes;
00775             storage1[j] = invCenterIndex[index];
00776             storage2[j] = Dij[index][i];
00777         }
00778 
00779         storage1 += num_neighbors;
00780         storage2 += num_neighbors;
00781         available_space -= num_neighbors;
00782     }
00783 
00784     free(dist);
00785     free(visited_nodes);
00786 
00787 
00788     if (Dij != NULL) {
00789         free(Dij[0]);
00790         free(Dij);
00791     }
00792 
00793         /*************************************************
00794            Laplacian computation   
00795         *************************************************/
00796 
00797     lap = N_GNEW(n, vtx_data);
00798     edges = N_GNEW(nedges + n, int);
00799     ewgts = N_GNEW(nedges + n, float);
00800     for (i = 0; i < n; i++) {
00801         lap[i].edges = edges;
00802         lap[i].ewgts = ewgts;
00803         lap[i].nedges = distances[i].nedges + 1;        /*add the self loop */
00804         dist_list = distances[i].edist - 1;     /* '-1' since edist[0] goes for number '1' entry in the lap */
00805         degree = 0;
00806         for (j = 1; j < lap[i].nedges; j++) {
00807             edges[j] = distances[i].edges[j - 1];
00808 #ifdef Dij2
00809             ewgts[j] = (float) -1.0 / ((float) dist_list[j] * (float) dist_list[j]);    /* cast to float to prevent overflow */
00810 #else
00811             ewgts[j] = -1.0 / (float) dist_list[j];
00812 #endif
00813             degree -= ewgts[j];
00814         }
00815         edges[0] = i;
00816         ewgts[0] = (float) degree;
00817         edges += lap[i].nedges;
00818         ewgts += lap[i].nedges;
00819     }
00820 
00821         /*************************************************
00822            Layout optimization    
00823         *************************************************/
00824 
00825     b = N_GNEW(n, double);
00826     old_stress = compute_stress1(coords, distances, dim, n), new_stress;
00827     for (converged = FALSE, iterations = 0;
00828          iterations < n_iterations && !converged; iterations++) {
00829 
00830         /* Axis-by-axis optimization: */
00831         for (k = 0; k < dim; k++) {
00832             /* compute the vector b */
00833             /* multiply on-the-fly with distance-based laplacian */
00834             /* (for saving storage we don't construct this Lap explicitly) */
00835             for (i = 0; i < n; i++) {
00836                 degree = 0;
00837                 b[i] = 0;
00838                 dist_list = distances[i].edist - 1;
00839                 edges = lap[i].edges;
00840                 ewgts = lap[i].ewgts;
00841                 for (j = 1; j < lap[i].nedges; j++) {
00842                     node = edges[j];
00843                     dist_ij = distance_kD(coords, dim, i, node);
00844                     if (dist_ij > 1e-30) {      /* skip zero distances */
00845                         L_ij = -ewgts[j] * dist_list[j] / dist_ij;      /* L_ij=w_{ij}*d_{ij}/dist_{ij} */
00846                         degree -= L_ij;
00847                         b[i] += L_ij * coords[k][node];
00848                     }
00849                 }
00850                 b[i] += degree * coords[k][i];
00851             }
00852             conjugate_gradient(lap, coords[k], b, n, conj_tol, n);
00853         }
00854 
00855         if ((converged = (iterations % 2 == 0))) {      /* check for convergence each two iterations */
00856             new_stress = compute_stress1(coords, distances, dim, n);
00857             converged =
00858                 fabs(new_stress - old_stress) / (new_stress + 1e-10) <
00859                 Epsilon;
00860             old_stress = new_stress;
00861         }
00862     }
00863     free(b);
00864 
00865     if (smooth_pivots) {
00866         /* relocate the pivots, so they do not break out of the layout */
00867         local_beautify_kD(invCenterIndex, num_centers, graph, n,
00868                           dist_bound, reweight_graph, coords, dim);
00869     }
00870 
00871     if (reweight_graph) {       /* do it only after the local beautification */
00872         restore_old_weights(graph, n, old_weights);
00873     }
00874 
00875     for (i = 0; i < n; i++) {
00876         if (distances[i].free_mem) {
00877             free(distances[i].edges);
00878             free(distances[i].edist);
00879         }
00880     }
00881 
00882     free(distances);
00883     free(lap[0].edges);
00884     free(lap[0].ewgts);
00885     free(lap);
00886     free(CenterIndex);
00887     free(invCenterIndex);
00888     freeQueue(&Q);
00889     return iterations;
00890 }
00891 #endif                          /* UNUSED */
00892 
00893 /* sparse_stress_subspace_majorization_kD:
00894  *
00895  * NOTE: We use integral shortest path values here, assuming
00896  * this is only to get an initial layout. In general, if edge lengths
00897  * are involved, we may end up with 0 length edges. 
00898  */
00899 static int sparse_stress_subspace_majorization_kD(vtx_data * graph,     /* Input graph in sparse representation */
00900                                                   int n,        /* Number of nodes */
00901                                                   int nedges_graph,     /* Number of edges */
00902                                                   double **coords,      /* coordinates of nodes (output layout)  */
00903                                                   int dim,      /* dimemsionality of layout */
00904                                                   int smart_ini,        /* smart initialization */
00905                                                   int reweight_graph,   /* difference model */
00906                                                   int n_iterations,     /* max #iterations */
00907                                                   int dist_bound,       /* neighborhood size in sparse distance matrix    */
00908                                                   int num_centers       /* #pivots in sparse distance matrix  */
00909     )
00910 {
00911     int iterations;             /* output: number of iteration of the process */
00912 
00913     double conj_tol = tolerance_cg;     /* tolerance of Conjugate Gradient */
00914 
00915         /*************************************************
00916         ** Computation of pivot-based, sparse, subspace-restricted **
00917         ** k-D  stress minimization by majorization                **    
00918         *************************************************/
00919 
00920     int i, j, k, node;
00921 
00922         /*************************************************
00923         ** First compute the subspace in which we optimize     **
00924         ** The subspace is  the high-dimensional embedding     **
00925         *************************************************/
00926 
00927     int subspace_dim = MIN(stress_pca_dim, n);  /* overall dimensionality of subspace */
00928     double **subspace = N_GNEW(subspace_dim, double *);
00929     double *d_storage = N_GNEW(subspace_dim * n, double);
00930     int num_centers_local;
00931     DistType **full_coords;
00932     /* if i is a pivot than CenterIndex[i] is its index, otherwise CenterIndex[i]= -1 */
00933     int *CenterIndex;
00934     int *invCenterIndex;        /* list the pivot nodes  */
00935     Queue Q;
00936     float *old_weights;
00937     /* this matrix stores the distance between  each node and each "center" */
00938     DistType **Dij;
00939     /* this vector stores the distances of each node to the selected "centers" */
00940     DistType *dist;
00941     DistType max_dist;
00942     DistType *storage;
00943     int *visited_nodes;
00944     dist_data *distances;
00945     int available_space;
00946     int *storage1 = NULL;
00947     DistType *storage2 = NULL;
00948     int num_visited_nodes;
00949     int num_neighbors;
00950     int index;
00951     int nedges;
00952     DistType *dist_list;
00953     vtx_data *lap;
00954     int *edges;
00955     float *ewgts;
00956     double degree;
00957     double **directions;
00958     float **tmp_mat;
00959     float **matrix;
00960     double dist_ij;
00961     double *b;
00962     double *b_restricted;
00963     double L_ij;
00964     double old_stress, new_stress;
00965     boolean converged;
00966 
00967     for (i = 0; i < subspace_dim; i++) {
00968         subspace[i] = d_storage + i * n;
00969     }
00970 
00971     /* compute PHDE: */
00972     num_centers_local = MIN(n, MAX(2 * subspace_dim, 50));
00973     full_coords = NULL;
00974     /* High dimensional embedding */
00975     embed_graph(graph, n, num_centers_local, &full_coords, reweight_graph);
00976     /* Centering coordinates */
00977     center_coordinate(full_coords, n, num_centers_local);
00978     /* PCA */
00979     PCA_alloc(full_coords, num_centers_local, n, subspace, subspace_dim);
00980 
00981     free(full_coords[0]);
00982     free(full_coords);
00983 
00984         /*************************************************
00985         ** Compute the sparse-shortest-distances matrix 'distances' **
00986         *************************************************/
00987 
00988     CenterIndex = N_GNEW(n, int);
00989     for (i = 0; i < n; i++) {
00990         CenterIndex[i] = -1;
00991     }
00992     invCenterIndex = NULL;
00993 
00994     mkQueue(&Q, n);
00995     old_weights = graph[0].ewgts;
00996 
00997     if (reweight_graph) {
00998         /* weight graph to separate high-degree nodes */
00999         /* in the future, perform slower Dijkstra-based computation */
01000         compute_new_weights(graph, n);
01001     }
01002 
01003     /* compute sparse distance matrix */
01004     /* first select 'num_centers' pivots from which we compute distance */
01005     /* to all other nodes */
01006 
01007     Dij = NULL;
01008     dist = N_GNEW(n, DistType);
01009     if (num_centers == 0) {     /* no pivots, skip pivots-to-nodes distance calculation */
01010         goto after_pivots_selection;
01011     }
01012 
01013     invCenterIndex = N_GNEW(num_centers, int);
01014 
01015     storage = N_GNEW(n * num_centers, DistType);
01016     Dij = N_GNEW(num_centers, DistType *);
01017     for (i = 0; i < num_centers; i++)
01018         Dij[i] = storage + i * n;
01019 
01020     /* select 'num_centers' pivots that are uniformaly spreaded over the graph */
01021 
01022     /* the first pivots is selected randomly */
01023     node = rand() % n;
01024     CenterIndex[node] = 0;
01025     invCenterIndex[0] = node;
01026 
01027     if (reweight_graph) {
01028         dijkstra(node, graph, n, Dij[0]);
01029     } else {
01030         bfs(node, graph, n, Dij[0], &Q);
01031     }
01032 
01033     /* find the most distant node from first pivot */
01034     max_dist = 0;
01035     for (i = 0; i < n; i++) {
01036         dist[i] = Dij[0][i];
01037         if (dist[i] > max_dist) {
01038             node = i;
01039             max_dist = dist[i];
01040         }
01041     }
01042     /* select other dim-1 nodes as pivots */
01043     for (i = 1; i < num_centers; i++) {
01044         CenterIndex[node] = i;
01045         invCenterIndex[i] = node;
01046         if (reweight_graph) {
01047             dijkstra(node, graph, n, Dij[i]);
01048         } else {
01049             bfs(node, graph, n, Dij[i], &Q);
01050         }
01051         max_dist = 0;
01052         for (j = 0; j < n; j++) {
01053             dist[j] = MIN(dist[j], Dij[i][j]);
01054             if (dist[j] > max_dist
01055                 || (dist[j] == max_dist && rand() % (j + 1) == 0)) {
01056                 node = j;
01057                 max_dist = dist[j];
01058             }
01059         }
01060     }
01061 
01062   after_pivots_selection:
01063 
01064     /* Construct a sparse distance matrix 'distances' */
01065 
01066     /* initialize dist to -1, important for 'bfs_bounded(..)' */
01067     for (i = 0; i < n; i++) {
01068         dist[i] = -1;
01069     }
01070 
01071     visited_nodes = N_GNEW(n, int);
01072     distances = N_GNEW(n, dist_data);
01073     available_space = 0;
01074     nedges = 0;
01075     for (i = 0; i < n; i++) {
01076         if (CenterIndex[i] >= 0) {      /* a pivot node */
01077             distances[i].edges = N_GNEW(n - 1, int);
01078             distances[i].edist = N_GNEW(n - 1, DistType);
01079             distances[i].nedges = n - 1;
01080             nedges += n - 1;
01081             distances[i].free_mem = TRUE;
01082             index = CenterIndex[i];
01083             for (j = 0; j < i; j++) {
01084                 distances[i].edges[j] = j;
01085                 distances[i].edist[j] = Dij[index][j];
01086             }
01087             for (j = i + 1; j < n; j++) {
01088                 distances[i].edges[j - 1] = j;
01089                 distances[i].edist[j - 1] = Dij[index][j];
01090             }
01091             continue;
01092         }
01093 
01094         /* a non pivot node */
01095 
01096         if (dist_bound > 0) {
01097             if (reweight_graph) {
01098                 num_visited_nodes =
01099                     dijkstra_bounded(i, graph, n, dist, dist_bound,
01100                                      visited_nodes);
01101             } else {
01102                 num_visited_nodes =
01103                     bfs_bounded(i, graph, n, dist, &Q, dist_bound,
01104                                 visited_nodes);
01105             }
01106             /* filter the pivots out of the visited nodes list, and the self loop: */
01107             for (j = 0; j < num_visited_nodes;) {
01108                 if (CenterIndex[visited_nodes[j]] < 0
01109                     && visited_nodes[j] != i) {
01110                     /* not a pivot or self loop */
01111                     j++;
01112                 } else {
01113                     dist[visited_nodes[j]] = -1;
01114                     visited_nodes[j] = visited_nodes[--num_visited_nodes];
01115                 }
01116             }
01117         } else {
01118             num_visited_nodes = 0;
01119         }
01120         num_neighbors = num_visited_nodes + num_centers;
01121         if (num_neighbors > available_space) {
01122             available_space = (dist_bound + 1) * n;
01123             storage1 = N_GNEW(available_space, int);
01124             storage2 = N_GNEW(available_space, DistType);
01125             distances[i].free_mem = TRUE;
01126         } else {
01127             distances[i].free_mem = FALSE;
01128         }
01129         distances[i].edges = storage1;
01130         distances[i].edist = storage2;
01131         distances[i].nedges = num_neighbors;
01132         nedges += num_neighbors;
01133         for (j = 0; j < num_visited_nodes; j++) {
01134             storage1[j] = visited_nodes[j];
01135             storage2[j] = dist[visited_nodes[j]];
01136             dist[visited_nodes[j]] = -1;
01137         }
01138         /* add all pivots: */
01139         for (j = num_visited_nodes; j < num_neighbors; j++) {
01140             index = j - num_visited_nodes;
01141             storage1[j] = invCenterIndex[index];
01142             storage2[j] = Dij[index][i];
01143         }
01144 
01145         storage1 += num_neighbors;
01146         storage2 += num_neighbors;
01147         available_space -= num_neighbors;
01148     }
01149 
01150     free(dist);
01151     free(visited_nodes);
01152 
01153     if (Dij != NULL) {
01154         free(Dij[0]);
01155         free(Dij);
01156     }
01157 
01158         /*************************************************
01159         ** Laplacian computation **
01160         *************************************************/
01161 
01162     lap = N_GNEW(n, vtx_data);
01163     edges = N_GNEW(nedges + n, int);
01164     ewgts = N_GNEW(nedges + n, float);
01165     for (i = 0; i < n; i++) {
01166         lap[i].edges = edges;
01167         lap[i].ewgts = ewgts;
01168         lap[i].nedges = distances[i].nedges + 1;        /*add the self loop */
01169         dist_list = distances[i].edist - 1;     /* '-1' since edist[0] goes for number '1' entry in the lap */
01170         degree = 0;
01171         for (j = 1; j < lap[i].nedges; j++) {
01172             edges[j] = distances[i].edges[j - 1];
01173 #ifdef Dij2
01174             ewgts[j] = (float) -1.0 / ((float) dist_list[j] * (float) dist_list[j]);    /* cast to float to prevent overflow */
01175 #else
01176             ewgts[j] = -1.0 / (float) dist_list[j];
01177 #endif
01178             degree -= ewgts[j];
01179         }
01180         edges[0] = i;
01181         ewgts[0] = (float) degree;
01182         edges += lap[i].nedges;
01183         ewgts += lap[i].nedges;
01184     }
01185 
01186         /*************************************************
01187         ** initialize direction vectors  **
01188         ** to get an intial layout       **
01189         *************************************************/
01190 
01191     /* the layout is subspace*directions */
01192     directions = N_GNEW(dim, double *);
01193     directions[0] = N_GNEW(dim * subspace_dim, double);
01194     for (i = 1; i < dim; i++) {
01195         directions[i] = directions[0] + i * subspace_dim;
01196     }
01197 
01198     if (smart_ini) {
01199         /* smart initialization */
01200         for (k = 0; k < dim; k++) {
01201             for (i = 0; i < subspace_dim; i++) {
01202                 directions[k][i] = 0;
01203             }
01204         }
01205         if (dim != 2) {
01206             /* use the first vectors in the eigenspace */
01207             /* each direction points to its "principal axes" */
01208             for (k = 0; k < dim; k++) {
01209                 directions[k][k] = 1;
01210             }
01211         } else {
01212             /* for the frequent 2-D case we prefer iterative-PCA over PCA */
01213             /* Note that we don't want to mix the Lap's eigenspace with the HDE */
01214             /* in the computation since they have different scales */
01215 
01216             directions[0][0] = 1;       /* first pca projection vector */
01217             if (!iterativePCA_1D(subspace, subspace_dim, n, directions[1])) {
01218                 for (k = 0; k < subspace_dim; k++) {
01219                     directions[1][k] = 0;
01220                 }
01221                 directions[1][1] = 1;
01222             }
01223         }
01224 
01225     } else {
01226         /* random initialization */
01227         for (k = 0; k < dim; k++) {
01228             for (i = 0; i < subspace_dim; i++) {
01229                 directions[k][i] = (double) (rand()) / RAND_MAX;
01230             }
01231         }
01232     }
01233 
01234     /* compute initial k-D layout */
01235 
01236     for (k = 0; k < dim; k++) {
01237         right_mult_with_vector_transpose(subspace, n, subspace_dim,
01238                                          directions[k], coords[k]);
01239     }
01240 
01241         /*************************************************
01242         ** compute restriction of the laplacian to subspace: ** 
01243         *************************************************/
01244 
01245     tmp_mat = NULL;
01246     matrix = NULL;
01247     mult_sparse_dense_mat_transpose(lap, subspace, n, subspace_dim,
01248                                     &tmp_mat);
01249     mult_dense_mat(subspace, tmp_mat, subspace_dim, n, subspace_dim,
01250                    &matrix);
01251     free(tmp_mat[0]);
01252     free(tmp_mat);
01253 
01254         /*************************************************
01255         ** Layout optimization  **
01256         *************************************************/
01257 
01258     b = N_GNEW(n, double);
01259     b_restricted = N_GNEW(subspace_dim, double);
01260     old_stress = compute_stress1(coords, distances, dim, n);
01261     for (converged = FALSE, iterations = 0;
01262          iterations < n_iterations && !converged; iterations++) {
01263 
01264         /* Axis-by-axis optimization: */
01265         for (k = 0; k < dim; k++) {
01266             /* compute the vector b */
01267             /* multiply on-the-fly with distance-based laplacian */
01268             /* (for saving storage we don't construct this Lap explicitly) */
01269             for (i = 0; i < n; i++) {
01270                 degree = 0;
01271                 b[i] = 0;
01272                 dist_list = distances[i].edist - 1;
01273                 edges = lap[i].edges;
01274                 ewgts = lap[i].ewgts;
01275                 for (j = 1; j < lap[i].nedges; j++) {
01276                     node = edges[j];
01277                     dist_ij = distance_kD(coords, dim, i, node);
01278                     if (dist_ij > 1e-30) {      /* skip zero distances */
01279                         L_ij = -ewgts[j] * dist_list[j] / dist_ij;      /* L_ij=w_{ij}*d_{ij}/dist_{ij} */
01280                         degree -= L_ij;
01281                         b[i] += L_ij * coords[k][node];
01282                     }
01283                 }
01284                 b[i] += degree * coords[k][i];
01285             }
01286             right_mult_with_vector_d(subspace, subspace_dim, n, b,
01287                                      b_restricted);
01288             conjugate_gradient_f(matrix, directions[k], b_restricted,
01289                                  subspace_dim, conj_tol, subspace_dim,
01290                                  FALSE);
01291             right_mult_with_vector_transpose(subspace, n, subspace_dim,
01292                                              directions[k], coords[k]);
01293         }
01294 
01295         if ((converged = (iterations % 2 == 0))) {      /* check for convergence each two iterations */
01296             new_stress = compute_stress1(coords, distances, dim, n);
01297             converged =
01298                 fabs(new_stress - old_stress) / (new_stress + 1e-10) <
01299                 Epsilon;
01300             old_stress = new_stress;
01301         }
01302     }
01303     free(b_restricted);
01304     free(b);
01305 
01306     if (reweight_graph) {
01307         restore_old_weights(graph, n, old_weights);
01308     }
01309 
01310     for (i = 0; i < n; i++) {
01311         if (distances[i].free_mem) {
01312             free(distances[i].edges);
01313             free(distances[i].edist);
01314         }
01315     }
01316 
01317     free(distances);
01318     free(lap[0].edges);
01319     free(lap[0].ewgts);
01320     free(lap);
01321     free(CenterIndex);
01322     free(invCenterIndex);
01323     free(directions[0]);
01324     free(directions);
01325     if (matrix != NULL) {
01326         free(matrix[0]);
01327         free(matrix);
01328     }
01329     free(subspace[0]);
01330     free(subspace);
01331     freeQueue(&Q);
01332 
01333     return iterations;
01334 }
01335 
01336 /* compute_weighted_apsp_packed:
01337  * Edge lengths can be any float > 0
01338  */
01339 static float *compute_weighted_apsp_packed(vtx_data * graph, int n)
01340 {
01341     int i, j, count;
01342     float *Dij = N_NEW(n * (n + 1) / 2, float);
01343 
01344     float *Di = N_NEW(n, float);
01345     Queue Q;
01346 
01347     mkQueue(&Q, n);
01348 
01349     count = 0;
01350     for (i = 0; i < n; i++) {
01351         dijkstra_f(i, graph, n, Di);
01352         for (j = i; j < n; j++) {
01353             Dij[count++] = Di[j];
01354         }
01355     }
01356     free(Di);
01357     freeQueue(&Q);
01358     return Dij;
01359 }
01360 
01361 /* compute_apsp_packed:
01362  * Assumes integral weights > 0.
01363  */
01364 float *compute_apsp_packed(vtx_data * graph, int n)
01365 {
01366     int i, j, count;
01367     float *Dij = N_NEW(n * (n + 1) / 2, float);
01368 
01369     DistType *Di = N_NEW(n, DistType);
01370     Queue Q;
01371 
01372     mkQueue(&Q, n);
01373 
01374     count = 0;
01375     for (i = 0; i < n; i++) {
01376         bfs(i, graph, n, Di, &Q);
01377         for (j = i; j < n; j++) {
01378             Dij[count++] = ((float) Di[j]);
01379         }
01380     }
01381     free(Di);
01382     freeQueue(&Q);
01383     return Dij;
01384 }
01385 
01386 #define max(x,y) ((x)>(y)?(x):(y))
01387 
01388 float *compute_apsp_artifical_weights_packed(vtx_data * graph, int n)
01389 {
01390     /* compute all-pairs-shortest-path-length while weighting the graph */
01391     /* so high-degree nodes are distantly located */
01392 
01393     float *Dij;
01394     int i, j;
01395     float *old_weights = graph[0].ewgts;
01396     int nedges = 0;
01397     float *weights;
01398     int *vtx_vec;
01399     int deg_i, deg_j, neighbor;
01400 
01401     for (i = 0; i < n; i++) {
01402         nedges += graph[i].nedges;
01403     }
01404 
01405     weights = N_NEW(nedges, float);
01406     vtx_vec = N_NEW(n, int);
01407     for (i = 0; i < n; i++) {
01408         vtx_vec[i] = 0;
01409     }
01410 
01411     if (graph->ewgts) {
01412         for (i = 0; i < n; i++) {
01413             fill_neighbors_vec_unweighted(graph, i, vtx_vec);
01414             deg_i = graph[i].nedges - 1;
01415             for (j = 1; j <= deg_i; j++) {
01416                 neighbor = graph[i].edges[j];
01417                 deg_j = graph[neighbor].nedges - 1;
01418                 weights[j] =
01419                     (float)
01420                     max((float)
01421                         (deg_i + deg_j -
01422                          2 * common_neighbors(graph, i, neighbor,
01423                                               vtx_vec)),
01424                         graph[i].ewgts[j]);
01425             }
01426             empty_neighbors_vec(graph, i, vtx_vec);
01427             graph[i].ewgts = weights;
01428             weights += graph[i].nedges;
01429         }
01430         Dij = compute_weighted_apsp_packed(graph, n);
01431     } else {
01432         for (i = 0; i < n; i++) {
01433             graph[i].ewgts = weights;
01434             fill_neighbors_vec_unweighted(graph, i, vtx_vec);
01435             deg_i = graph[i].nedges - 1;
01436             for (j = 1; j <= deg_i; j++) {
01437                 neighbor = graph[i].edges[j];
01438                 deg_j = graph[neighbor].nedges - 1;
01439                 weights[j] =
01440                     ((float) deg_i + deg_j -
01441                      2 * common_neighbors(graph, i, neighbor, vtx_vec));
01442             }
01443             empty_neighbors_vec(graph, i, vtx_vec);
01444             weights += graph[i].nedges;
01445         }
01446         Dij = compute_apsp_packed(graph, n);
01447     }
01448 
01449     free(vtx_vec);
01450     free(graph[0].ewgts);
01451     graph[0].ewgts = NULL;
01452     if (old_weights != NULL) {
01453         for (i = 0; i < n; i++) {
01454             graph[i].ewgts = old_weights;
01455             old_weights += graph[i].nedges;
01456         }
01457     }
01458     return Dij;
01459 }
01460 
01461 /* Accumulator type for diagonal of Laplacian. Needs to be as large
01462  * as possible. Use long double; configure to double if necessary.
01463  */
01464 #define DegType long double
01465 
01466 /* stress_majorization_kD_mkernel:
01467  * At present, if any nodes have pos set, smart_ini is false.
01468  */
01469 int stress_majorization_kD_mkernel(vtx_data * graph,    /* Input graph in sparse representation */
01470                                    int n,       /* Number of nodes */
01471                                    int nedges_graph,    /* Number of edges */
01472                                    double **d_coords,   /* coordinates of nodes (output layout) */
01473                                    int dim,     /* dimemsionality of layout */
01474                                    int smart_ini,       /* smart initialization */
01475                                    int model,   /* model */
01476                                    int maxi     /* max iterations */
01477     )
01478 {
01479     int iterations;             /* output: number of iteration of the process */
01480 
01481     double conj_tol = tolerance_cg;     /* tolerance of Conjugate Gradient */
01482     float *Dij = NULL;
01483     int i, j, k;
01484     float **coords;
01485     float *f_storage;
01486     float constant_term;
01487     int count;
01488     DegType degree;
01489     int lap_length;
01490     float *lap2;
01491     DegType *degrees;
01492     int step;
01493     float val;
01494     double old_stress, new_stress;
01495     boolean converged;
01496     float **b;
01497     float *tmp_coords;
01498     float *dist_accumulator;
01499     float *lap1;
01500     int len;
01501     int havePinned;             /* some node is pinned */
01502 #ifdef ALTERNATIVE_STRESS_CALC
01503     double mat_stress;
01504 #endif
01505 
01506         /*************************************************
01507         ** Computation of full, dense, unrestricted k-D ** 
01508         ** stress minimization by majorization          **    
01509         *************************************************/
01510 
01511         /****************************************************
01512         ** Compute the all-pairs-shortest-distances matrix **
01513         ****************************************************/
01514 
01515     if (maxi == 0)
01516         return 0;
01517 
01518     if (Verbose)
01519         start_timer();
01520 
01521     if (model == MODEL_SUBSET) {
01522         /* weight graph to separate high-degree nodes */
01523         /* and perform slower Dijkstra-based computation */
01524         if (Verbose)
01525             fprintf(stderr, "Calculating subset model");
01526         Dij = compute_apsp_artifical_weights_packed(graph, n);
01527     } else if (model == MODEL_CIRCUIT) {
01528         Dij = circuitModel(graph, n);
01529         if (!Dij) {
01530             agerr(AGWARN,
01531                   "graph is disconnected. Hence, the circuit model\n");
01532             agerr(AGPREV,
01533                   "is undefined. Reverting to the shortest path model.\n");
01534         }
01535     }
01536     if (!Dij) {
01537         if (Verbose)
01538             fprintf(stderr, "Calculating shortest paths");
01539         if (graph->ewgts)
01540             Dij = compute_weighted_apsp_packed(graph, n);
01541         else
01542             Dij = compute_apsp_packed(graph, n);
01543     }
01544 
01545     if (Verbose) {
01546         fprintf(stderr, ": %.2f sec\n", elapsed_sec());
01547         fprintf(stderr, "Setting initial positions");
01548         start_timer();
01549     }
01550 
01551         /**************************
01552         ** Layout initialization **
01553         **************************/
01554 
01555     if (smart_ini && (n > 1)) {
01556         havePinned = 0;
01557         /* optimize layout quickly within subspace */
01558         /* perform at most 50 iterations within 30-D subspace to 
01559            get an estimate */
01560         sparse_stress_subspace_majorization_kD(graph, n, nedges_graph,
01561                                                d_coords, dim, smart_ini,
01562                                                (model == MODEL_SUBSET), 50,
01563                                                neighborhood_radius_subspace,
01564                                                num_pivots_stress);
01565 
01566         for (i = 0; i < dim; i++) {
01567             /* for numerical stability, scale down layout */
01568             double max = 1;
01569             for (j = 0; j < n; j++) {
01570                 if (fabs(d_coords[i][j]) > max) {
01571                     max = fabs(d_coords[i][j]);
01572                 }
01573             }
01574             for (j = 0; j < n; j++) {
01575                 d_coords[i][j] /= max;
01576             }
01577             /* add small random noise */
01578             for (j = 0; j < n; j++) {
01579                 d_coords[i][j] += 1e-6 * (drand48() - 0.5);
01580             }
01581             orthog1(n, d_coords[i]);
01582         }
01583     } else {
01584         havePinned = initLayout(graph, n, dim, d_coords);
01585     }
01586     if (Verbose) fprintf(stderr, ": %.2f sec", elapsed_sec());
01587     if (n == 1) return 0;
01588 
01589     if (Verbose) {
01590         fprintf(stderr, ": %.2f sec\n", elapsed_sec());
01591         fprintf(stderr, "Setting up stress function");
01592         start_timer();
01593     }
01594     coords = N_NEW(dim, float *);
01595     f_storage = N_NEW(dim * n, float);
01596     for (i = 0; i < dim; i++) {
01597         coords[i] = f_storage + i * n;
01598         for (j = 0; j < n; j++) {
01599             coords[i][j] = ((float) d_coords[i][j]);
01600         }
01601     }
01602 
01603     /* compute constant term in stress sum */
01604     /* which is \sum_{i<j} w_{ij}d_{ij}^2 */
01605 #ifdef Dij2
01606     constant_term = ((float) n * (n - 1) / 2);
01607 #else
01608     constant_term = 0;
01609     for (count = 0, i = 0; i < n - 1; i++) {
01610         count++;                /* skip self distance */
01611         for (j = 1; j < n - i; j++, count++) {
01612             constant_term += Dij[count];
01613         }
01614     }
01615 #endif
01616 
01617         /**************************
01618         ** Laplacian computation **
01619         **************************/
01620 
01621     lap_length = n * (n + 1) / 2;
01622     lap2 = Dij;
01623 #ifdef Dij2
01624     square_vec(lap_length, lap2);
01625 #endif
01626     /* compute off-diagonal entries */
01627     invert_vec(lap_length, lap2);
01628 
01629     /* compute diagonal entries */
01630     count = 0;
01631     degrees = N_NEW(n, DegType);
01632     /* set_vector_val(n, 0, degrees); */
01633     memset(degrees, 0, n * sizeof(DegType));
01634     for (i = 0; i < n - 1; i++) {
01635         degree = 0;
01636         count++;                /* skip main diag entry */
01637         for (j = 1; j < n - i; j++, count++) {
01638             val = lap2[count];
01639             degree += val;
01640             degrees[i + j] -= val;
01641         }
01642         degrees[i] -= degree;
01643     }
01644     for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) {
01645         lap2[count] = degrees[i];
01646     }
01647 
01648 #ifdef NONCORE
01649     if (n > max_nodes_in_mem) {
01650 #define FILENAME "tmp_Dij$$$.bin"
01651         fp = fopen(FILENAME, "wb");
01652         fwrite(lap2, sizeof(float), lap_length, fp);
01653         fclose(fp);
01654     }
01655 #endif
01656 
01657         /*************************
01658         ** Layout optimization  **
01659         *************************/
01660 
01661     b = N_NEW(dim, float *);
01662     b[0] = N_NEW(dim * n, float);
01663     for (k = 1; k < dim; k++) {
01664         b[k] = b[0] + k * n;
01665     }
01666 
01667     tmp_coords = N_NEW(n, float);
01668     dist_accumulator = N_NEW(n, float);
01669     lap1 = NULL;
01670 #ifdef NONCORE
01671     if (n <= max_nodes_in_mem) {
01672         lap1 = N_NEW(lap_length, float);
01673     } else {
01674         lap1 = lap2;
01675         fp = fopen(FILENAME, "rb");
01676         fgetpos(fp, &pos);
01677     }
01678 #else
01679     lap1 = N_NEW(lap_length, float);
01680 #endif
01681 
01682 
01683     old_stress = MAXDOUBLE;     /* at least one iteration */
01684     if (Verbose) {
01685         fprintf(stderr, ": %.2f sec\n", elapsed_sec());
01686         fprintf(stderr, "Solving model: ");
01687         start_timer();
01688     }
01689 
01690     for (converged = FALSE, iterations = 0;
01691          iterations < maxi && !converged; iterations++) {
01692 
01693         /* First, construct Laplacian of 1/(d_ij*|p_i-p_j|)  */
01694         /* set_vector_val(n, 0, degrees); */
01695         memset(degrees, 0, n * sizeof(DegType));
01696 #ifdef Dij2
01697 #ifdef NONCORE
01698         if (n <= max_nodes_in_mem) {
01699             sqrt_vecf(lap_length, lap2, lap1);
01700         } else {
01701             sqrt_vec(lap_length, lap1);
01702         }
01703 #else
01704         sqrt_vecf(lap_length, lap2, lap1);
01705 #endif
01706 #endif
01707         for (count = 0, i = 0; i < n - 1; i++) {
01708             len = n - i - 1;
01709             /* init 'dist_accumulator' with zeros */
01710             set_vector_valf(len, 0, dist_accumulator);
01711 
01712             /* put into 'dist_accumulator' all squared distances between 'i' and 'i'+1,...,'n'-1 */
01713             for (k = 0; k < dim; k++) {
01714                 set_vector_valf(len, coords[k][i], tmp_coords);
01715                 vectors_mult_additionf(len, tmp_coords, -1,
01716                                        coords[k] + i + 1);
01717                 square_vec(len, tmp_coords);
01718                 vectors_additionf(len, tmp_coords, dist_accumulator,
01719                                   dist_accumulator);
01720             }
01721 
01722             /* convert to 1/d_{ij} */
01723             invert_sqrt_vec(len, dist_accumulator);
01724             /* detect overflows */
01725             for (j = 0; j < len; j++) {
01726                 if (dist_accumulator[j] >= MAXFLOAT
01727                     || dist_accumulator[j] < 0) {
01728                     dist_accumulator[j] = 0;
01729                 }
01730             }
01731 
01732             count++;            /* save place for the main diagonal entry */
01733             degree = 0;
01734             for (j = 0; j < len; j++, count++) {
01735 #ifdef Dij2
01736                 val = lap1[count] *= dist_accumulator[j];
01737 #else
01738                 val = lap1[count] = dist_accumulator[j];
01739 #endif
01740                 degree += val;
01741                 degrees[i + j + 1] -= val;
01742             }
01743             degrees[i] -= degree;
01744         }
01745         for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) {
01746             lap1[count] = degrees[i];
01747         }
01748 
01749         /* Now compute b[] */
01750         for (k = 0; k < dim; k++) {
01751             /* b[k] := lap1*coords[k] */
01752             right_mult_with_vector_ff(lap1, n, coords[k], b[k]);
01753         }
01754 
01755 
01756         /* compute new stress  */
01757         /* remember that the Laplacians are negated, so we subtract instead of add and vice versa */
01758         new_stress = 0;
01759         for (k = 0; k < dim; k++) {
01760             new_stress += vectors_inner_productf(n, coords[k], b[k]);
01761         }
01762         new_stress *= 2;
01763         new_stress += constant_term;    /* only after mult by 2 */
01764 #ifdef NONCORE
01765         if (n > max_nodes_in_mem) {
01766             /* restore lap2 from memory */
01767             fsetpos(fp, &pos);
01768             fread(lap2, sizeof(float), lap_length, fp);
01769         }
01770 #endif
01771         for (k = 0; k < dim; k++) {
01772             right_mult_with_vector_ff(lap2, n, coords[k], tmp_coords);
01773             new_stress -= vectors_inner_productf(n, coords[k], tmp_coords);
01774         }
01775 #ifdef ALTERNATIVE_STRESS_CALC
01776         mat_stress = new_stress;
01777         new_stress = compute_stressf(coords, lap2, dim, n);
01778         if (fabs(mat_stress - new_stress) / min(mat_stress, new_stress) >
01779             0.001) {
01780             fprintf(stderr, "Diff stress vals: %lf %lf (iteration #%d)\n",
01781                     mat_stress, new_stress, iterations);
01782         }
01783 #endif
01784         /* Invariant: old_stress > 0. In theory, old_stress >= new_stress
01785          * but we use fabs in case of numerical error.
01786          */
01787         converged =
01788             (((fabs(old_stress - new_stress) / old_stress) < Epsilon)
01789              || (new_stress < Epsilon));
01790         old_stress = new_stress;
01791 
01792         for (k = 0; k < dim; k++) {
01793             node_t *np;
01794             if (havePinned) {
01795                 copy_vectorf(n, coords[k], tmp_coords);
01796                 conjugate_gradient_mkernel(lap2, tmp_coords, b[k], n,
01797                                            conj_tol, n);
01798                 for (i = 0; i < n; i++) {
01799                     np = graph[i].np;
01800                     if (!isFixed(np))
01801                         coords[k][i] = tmp_coords[i];
01802                 }
01803             } else {
01804                 conjugate_gradient_mkernel(lap2, coords[k], b[k], n,
01805                                            conj_tol, n);
01806             }
01807         }
01808         if (Verbose && (iterations % 5 == 0)) {
01809             fprintf(stderr, "%.3f ", new_stress);
01810             if ((iterations + 5) % 50 == 0)
01811                 fprintf(stderr, "\n");
01812         }
01813     }
01814     if (Verbose) {
01815         fprintf(stderr, "\nfinal e = %f %d iterations %.2f sec\n",
01816                 compute_stressf(coords, lap2, dim, n),
01817                 iterations, elapsed_sec());
01818     }
01819 
01820     for (i = 0; i < dim; i++) {
01821         for (j = 0; j < n; j++) {
01822             d_coords[i][j] = coords[i][j];
01823         }
01824     }
01825     free(coords[0]);
01826     free(coords);
01827 
01828     free(lap2);
01829     free(b[0]);
01830     free(b);
01831     free(tmp_coords);
01832     free(dist_accumulator);
01833     free(degrees);
01834     free(lap1);
01835     return iterations;
01836 }

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