Graphviz  2.31.20130618.0446
lib/neatogen/stress.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 #include "neato.h"
00016 #include "dijkstra.h"
00017 #include "bfs.h"
00018 #include "pca.h"
00019 #include "matrix_ops.h"
00020 #include "conjgrad.h"
00021 #include "embed_graph.h"
00022 #include "kkutils.h"
00023 #include "stress.h"
00024 #include <math.h>
00025 #include <stdlib.h>
00026 #include <time.h>
00027 
00028 
00029 #ifndef HAVE_DRAND48
00030 extern double drand48(void);
00031 #endif
00032 
00033 #define Dij2                    /* If defined, the terms in the stress energy are normalized 
00034                                    by d_{ij}^{-2} otherwise, they are normalized by d_{ij}^{-1}
00035                                  */
00036 
00037 #ifdef NONCORE
00038 /* Set 'max_nodes_in_mem' so that 
00039  * 4*(max_nodes_in_mem^2) is smaller than the available memory (in bytes)
00040  * 4 = sizeof(float)
00041  */
00042 #define max_nodes_in_mem 18000
00043 #endif
00044 
00045  /* relevant when using sparse distance matrix not within subspace */
00046 #define smooth_pivots true
00047 
00048 /* dimensionality of subspace; relevant 
00049  * when optimizing within subspace) 
00050  */
00051 #define stress_pca_dim 50
00052 
00053  /* a structure used for storing sparse distance matrix */
00054 typedef struct {
00055     int nedges;
00056     int *edges;
00057     DistType *edist;
00058     boolean free_mem;
00059 } dist_data;
00060 
00061 static double compute_stressf(float **coords, float *lap, int dim, int n, int exp)
00062 {
00063     /* compute the overall stress */
00064 
00065     int i, j, l, neighbor, count;
00066     double sum, dist, Dij;
00067     sum = 0;
00068     for (count = 0, i = 0; i < n - 1; i++) {
00069         count++;                /* skip diagonal entry */
00070         for (j = 1; j < n - i; j++, count++) {
00071             dist = 0;
00072             neighbor = i + j;
00073             for (l = 0; l < dim; l++) {
00074                 dist +=
00075                     (coords[l][i] - coords[l][neighbor]) * (coords[l][i] -
00076                                                             coords[l]
00077                                                             [neighbor]);
00078             }
00079             dist = sqrt(dist);
00080             if (exp == 2) {
00081 #ifdef Dij2
00082                 Dij = 1.0 / sqrt(lap[count]);
00083                 sum += (Dij - dist) * (Dij - dist) * (lap[count]);
00084 #else
00085                 Dij = 1.0 / lap[count];
00086                 sum += (Dij - dist) * (Dij - dist) * (lap[count]);
00087 #endif
00088             } else {
00089                 Dij = 1.0 / lap[count];
00090                 sum += (Dij - dist) * (Dij - dist) * (lap[count]);
00091             }
00092         }
00093     }
00094 
00095     return sum;
00096 }
00097 
00098 static double
00099 compute_stress1(double **coords, dist_data * distances, int dim, int n, int exp)
00100 {
00101     /* compute the overall stress */
00102 
00103     int i, j, l, node;
00104     double sum, dist, Dij;
00105     sum = 0;
00106     if (exp == 2) {
00107         for (i = 0; i < n; i++) {
00108             for (j = 0; j < distances[i].nedges; j++) {
00109                 node = distances[i].edges[j];
00110                 if (node <= i) {
00111                     continue;
00112                 }
00113                 dist = 0;
00114                 for (l = 0; l < dim; l++) {
00115                     dist +=
00116                         (coords[l][i] - coords[l][node]) * (coords[l][i] -
00117                                                             coords[l]
00118                                                             [node]);
00119                 }
00120                 dist = sqrt(dist);
00121                 Dij = distances[i].edist[j];
00122 #ifdef Dij2
00123                 sum += (Dij - dist) * (Dij - dist) / (Dij * Dij);
00124 #else
00125                 sum += (Dij - dist) * (Dij - dist) / Dij;
00126 #endif
00127             }
00128         }
00129     } else {
00130         for (i = 0; i < n; i++) {
00131             for (j = 0; j < distances[i].nedges; j++) {
00132                 node = distances[i].edges[j];
00133                 if (node <= i) {
00134                     continue;
00135                 }
00136                 dist = 0;
00137                 for (l = 0; l < dim; l++) {
00138                     dist +=
00139                         (coords[l][i] - coords[l][node]) * (coords[l][i] -
00140                                                             coords[l]
00141                                                             [node]);
00142                 }
00143                 dist = sqrt(dist);
00144                 Dij = distances[i].edist[j];
00145                 sum += (Dij - dist) * (Dij - dist) / Dij;
00146             }
00147         }
00148     }
00149 
00150     return sum;
00151 }
00152 
00153 /* initLayout:
00154  * Initialize node coordinates. If the node already has
00155  * a position, use it.
00156  * Return true if some node is fixed.
00157  */
00158 int
00159 initLayout(vtx_data * graph, int n, int dim, double **coords,
00160            node_t ** nodes)
00161 {
00162     node_t *np;
00163     double *xp;
00164     double *yp;
00165     double *pt;
00166     int i, d;
00167     int pinned = 0;
00168 
00169     xp = coords[0];
00170     yp = coords[1];
00171     for (i = 0; i < n; i++) {
00172         np = nodes[i];
00173         if (hasPos(np)) {
00174             pt = ND_pos(np);
00175             *xp++ = *pt++;
00176             *yp++ = *pt++;
00177             if (dim > 2) {
00178                 for (d = 2; d < dim; d++)
00179                     coords[d][i] = *pt++;
00180             }
00181             if (isFixed(np))
00182                 pinned = 1;
00183         } else {
00184             *xp++ = drand48();
00185             *yp++ = drand48();
00186             if (dim > 2) {
00187                 for (d = 2; d < dim; d++)
00188                     coords[d][i] = drand48();
00189             }
00190         }
00191     }
00192 
00193     for (d = 0; d < dim; d++)
00194         orthog1(n, coords[d]);
00195 
00196     return pinned;
00197 }
00198 
00199 float *circuitModel(vtx_data * graph, int nG)
00200 {
00201     int i, j, e, rv, count;
00202     float *Dij = N_NEW(nG * (nG + 1) / 2, float);
00203     double **Gm;
00204     double **Gm_inv;
00205 
00206     Gm = new_array(nG, nG, 0.0);
00207     Gm_inv = new_array(nG, nG, 0.0);
00208 
00209     /* set non-diagonal entries */
00210     if (graph->ewgts) {
00211         for (i = 0; i < nG; i++) {
00212             for (e = 1; e < graph[i].nedges; e++) {
00213                 j = graph[i].edges[e];
00214                 /* conductance is 1/resistance */
00215                 Gm[i][j] = Gm[j][i] = -1.0 / graph[i].ewgts[e]; /* negate */
00216             }
00217         }
00218     } else {
00219         for (i = 0; i < nG; i++) {
00220             for (e = 1; e < graph[i].nedges; e++) {
00221                 j = graph[i].edges[e];
00222                 /* conductance is 1/resistance */
00223                 Gm[i][j] = Gm[j][i] = -1.0;     /* ewgts are all 1 */
00224             }
00225         }
00226     }
00227 
00228     rv = solveCircuit(nG, Gm, Gm_inv);
00229 
00230     if (rv) {
00231         float v;
00232         count = 0;
00233         for (i = 0; i < nG; i++) {
00234             for (j = i; j < nG; j++) {
00235                 if (i == j)
00236                     v = 0.0;
00237                 else
00238                     v = (float) (Gm_inv[i][i] + Gm_inv[j][j] -
00239                                  2.0 * Gm_inv[i][j]);
00240                 Dij[count++] = v;
00241             }
00242         }
00243     } else {
00244         free(Dij);
00245         Dij = NULL;
00246     }
00247     free_array(Gm);
00248     free_array(Gm_inv);
00249     return Dij;
00250 }
00251 
00252 /* sparse_stress_subspace_majorization_kD:
00253  * Optimization of the stress function using sparse distance matrix, within a vector-space
00254  * Fastest and least accurate method
00255  *
00256  * NOTE: We use integral shortest path values here, assuming
00257  * this is only to get an initial layout. In general, if edge lengths
00258  * are involved, we may end up with 0 length edges. 
00259  */
00260 static int sparse_stress_subspace_majorization_kD(vtx_data * graph,     /* Input graph in sparse representation */
00261                                                   int n,        /* Number of nodes */
00262                                                   int nedges_graph,     /* Number of edges */
00263                                                   double **coords,      /* coordinates of nodes (output layout)  */
00264                                                   int dim,      /* dimemsionality of layout */
00265                                                   int smart_ini,        /* smart initialization */
00266                                                   int exp,      /* scale exponent */
00267                                                   int reweight_graph,   /* difference model */
00268                                                   int n_iterations,     /* max #iterations */
00269                                                   int dist_bound,       /* neighborhood size in sparse distance matrix    */
00270                                                   int num_centers       /* #pivots in sparse distance matrix  */
00271     )
00272 {
00273     int iterations;             /* output: number of iteration of the process */
00274 
00275     double conj_tol = tolerance_cg;     /* tolerance of Conjugate Gradient */
00276 
00277         /*************************************************
00278         ** Computation of pivot-based, sparse, subspace-restricted **
00279         ** k-D  stress minimization by majorization                **    
00280         *************************************************/
00281 
00282     int i, j, k, node;
00283 
00284         /*************************************************
00285         ** First compute the subspace in which we optimize     **
00286         ** The subspace is  the high-dimensional embedding     **
00287         *************************************************/
00288 
00289     int subspace_dim = MIN(stress_pca_dim, n);  /* overall dimensionality of subspace */
00290     double **subspace = N_GNEW(subspace_dim, double *);
00291     double *d_storage = N_GNEW(subspace_dim * n, double);
00292     int num_centers_local;
00293     DistType **full_coords;
00294     /* if i is a pivot than CenterIndex[i] is its index, otherwise CenterIndex[i]= -1 */
00295     int *CenterIndex;
00296     int *invCenterIndex;        /* list the pivot nodes  */
00297     Queue Q;
00298     float *old_weights;
00299     /* this matrix stores the distance between  each node and each "center" */
00300     DistType **Dij;
00301     /* this vector stores the distances of each node to the selected "centers" */
00302     DistType *dist;
00303     DistType max_dist;
00304     DistType *storage;
00305     int *visited_nodes;
00306     dist_data *distances;
00307     int available_space;
00308     int *storage1 = NULL;
00309     DistType *storage2 = NULL;
00310     int num_visited_nodes;
00311     int num_neighbors;
00312     int index;
00313     int nedges;
00314     DistType *dist_list;
00315     vtx_data *lap;
00316     int *edges;
00317     float *ewgts;
00318     double degree;
00319     double **directions;
00320     float **tmp_mat;
00321     float **matrix;
00322     double dist_ij;
00323     double *b;
00324     double *b_restricted;
00325     double L_ij;
00326     double old_stress, new_stress;
00327     boolean converged;
00328 
00329     for (i = 0; i < subspace_dim; i++) {
00330         subspace[i] = d_storage + i * n;
00331     }
00332 
00333     /* compute PHDE: */
00334     num_centers_local = MIN(n, MAX(2 * subspace_dim, 50));
00335     full_coords = NULL;
00336     /* High dimensional embedding */
00337     embed_graph(graph, n, num_centers_local, &full_coords, reweight_graph);
00338     /* Centering coordinates */
00339     center_coordinate(full_coords, n, num_centers_local);
00340     /* PCA */
00341     PCA_alloc(full_coords, num_centers_local, n, subspace, subspace_dim);
00342 
00343     free(full_coords[0]);
00344     free(full_coords);
00345 
00346         /*************************************************
00347         ** Compute the sparse-shortest-distances matrix 'distances' **
00348         *************************************************/
00349 
00350     CenterIndex = N_GNEW(n, int);
00351     for (i = 0; i < n; i++) {
00352         CenterIndex[i] = -1;
00353     }
00354     invCenterIndex = NULL;
00355 
00356     mkQueue(&Q, n);
00357     old_weights = graph[0].ewgts;
00358 
00359     if (reweight_graph) {
00360         /* weight graph to separate high-degree nodes */
00361         /* in the future, perform slower Dijkstra-based computation */
00362         compute_new_weights(graph, n);
00363     }
00364 
00365     /* compute sparse distance matrix */
00366     /* first select 'num_centers' pivots from which we compute distance */
00367     /* to all other nodes */
00368 
00369     Dij = NULL;
00370     dist = N_GNEW(n, DistType);
00371     if (num_centers == 0) {     /* no pivots, skip pivots-to-nodes distance calculation */
00372         goto after_pivots_selection;
00373     }
00374 
00375     invCenterIndex = N_GNEW(num_centers, int);
00376 
00377     storage = N_GNEW(n * num_centers, DistType);
00378     Dij = N_GNEW(num_centers, DistType *);
00379     for (i = 0; i < num_centers; i++)
00380         Dij[i] = storage + i * n;
00381 
00382     /* select 'num_centers' pivots that are uniformaly spreaded over the graph */
00383 
00384     /* the first pivots is selected randomly */
00385     node = rand() % n;
00386     CenterIndex[node] = 0;
00387     invCenterIndex[0] = node;
00388 
00389     if (reweight_graph) {
00390         dijkstra(node, graph, n, Dij[0]);
00391     } else {
00392         bfs(node, graph, n, Dij[0], &Q);
00393     }
00394 
00395     /* find the most distant node from first pivot */
00396     max_dist = 0;
00397     for (i = 0; i < n; i++) {
00398         dist[i] = Dij[0][i];
00399         if (dist[i] > max_dist) {
00400             node = i;
00401             max_dist = dist[i];
00402         }
00403     }
00404     /* select other dim-1 nodes as pivots */
00405     for (i = 1; i < num_centers; i++) {
00406         CenterIndex[node] = i;
00407         invCenterIndex[i] = node;
00408         if (reweight_graph) {
00409             dijkstra(node, graph, n, Dij[i]);
00410         } else {
00411             bfs(node, graph, n, Dij[i], &Q);
00412         }
00413         max_dist = 0;
00414         for (j = 0; j < n; j++) {
00415             dist[j] = MIN(dist[j], Dij[i][j]);
00416             if (dist[j] > max_dist
00417                 || (dist[j] == max_dist && rand() % (j + 1) == 0)) {
00418                 node = j;
00419                 max_dist = dist[j];
00420             }
00421         }
00422     }
00423 
00424   after_pivots_selection:
00425 
00426     /* Construct a sparse distance matrix 'distances' */
00427 
00428     /* initialize dist to -1, important for 'bfs_bounded(..)' */
00429     for (i = 0; i < n; i++) {
00430         dist[i] = -1;
00431     }
00432 
00433     visited_nodes = N_GNEW(n, int);
00434     distances = N_GNEW(n, dist_data);
00435     available_space = 0;
00436     nedges = 0;
00437     for (i = 0; i < n; i++) {
00438         if (CenterIndex[i] >= 0) {      /* a pivot node */
00439             distances[i].edges = N_GNEW(n - 1, int);
00440             distances[i].edist = N_GNEW(n - 1, DistType);
00441             distances[i].nedges = n - 1;
00442             nedges += n - 1;
00443             distances[i].free_mem = TRUE;
00444             index = CenterIndex[i];
00445             for (j = 0; j < i; j++) {
00446                 distances[i].edges[j] = j;
00447                 distances[i].edist[j] = Dij[index][j];
00448             }
00449             for (j = i + 1; j < n; j++) {
00450                 distances[i].edges[j - 1] = j;
00451                 distances[i].edist[j - 1] = Dij[index][j];
00452             }
00453             continue;
00454         }
00455 
00456         /* a non pivot node */
00457 
00458         if (dist_bound > 0) {
00459             if (reweight_graph) {
00460                 num_visited_nodes =
00461                     dijkstra_bounded(i, graph, n, dist, dist_bound,
00462                                      visited_nodes);
00463             } else {
00464                 num_visited_nodes =
00465                     bfs_bounded(i, graph, n, dist, &Q, dist_bound,
00466                                 visited_nodes);
00467             }
00468             /* filter the pivots out of the visited nodes list, and the self loop: */
00469             for (j = 0; j < num_visited_nodes;) {
00470                 if (CenterIndex[visited_nodes[j]] < 0
00471                     && visited_nodes[j] != i) {
00472                     /* not a pivot or self loop */
00473                     j++;
00474                 } else {
00475                     dist[visited_nodes[j]] = -1;
00476                     visited_nodes[j] = visited_nodes[--num_visited_nodes];
00477                 }
00478             }
00479         } else {
00480             num_visited_nodes = 0;
00481         }
00482         num_neighbors = num_visited_nodes + num_centers;
00483         if (num_neighbors > available_space) {
00484             available_space = (dist_bound + 1) * n;
00485             storage1 = N_GNEW(available_space, int);
00486             storage2 = N_GNEW(available_space, DistType);
00487             distances[i].free_mem = TRUE;
00488         } else {
00489             distances[i].free_mem = FALSE;
00490         }
00491         distances[i].edges = storage1;
00492         distances[i].edist = storage2;
00493         distances[i].nedges = num_neighbors;
00494         nedges += num_neighbors;
00495         for (j = 0; j < num_visited_nodes; j++) {
00496             storage1[j] = visited_nodes[j];
00497             storage2[j] = dist[visited_nodes[j]];
00498             dist[visited_nodes[j]] = -1;
00499         }
00500         /* add all pivots: */
00501         for (j = num_visited_nodes; j < num_neighbors; j++) {
00502             index = j - num_visited_nodes;
00503             storage1[j] = invCenterIndex[index];
00504             storage2[j] = Dij[index][i];
00505         }
00506 
00507         storage1 += num_neighbors;
00508         storage2 += num_neighbors;
00509         available_space -= num_neighbors;
00510     }
00511 
00512     free(dist);
00513     free(visited_nodes);
00514 
00515     if (Dij != NULL) {
00516         free(Dij[0]);
00517         free(Dij);
00518     }
00519 
00520         /*************************************************
00521         ** Laplacian computation **
00522         *************************************************/
00523 
00524     lap = N_GNEW(n, vtx_data);
00525     edges = N_GNEW(nedges + n, int);
00526     ewgts = N_GNEW(nedges + n, float);
00527     for (i = 0; i < n; i++) {
00528         lap[i].edges = edges;
00529         lap[i].ewgts = ewgts;
00530         lap[i].nedges = distances[i].nedges + 1;        /*add the self loop */
00531         dist_list = distances[i].edist - 1;     /* '-1' since edist[0] goes for number '1' entry in the lap */
00532         degree = 0;
00533         if (exp == 2) {
00534             for (j = 1; j < lap[i].nedges; j++) {
00535                 edges[j] = distances[i].edges[j - 1];
00536 #ifdef Dij2
00537                 ewgts[j] = (float) -1.0 / ((float) dist_list[j] * (float) dist_list[j]);        /* cast to float to prevent overflow */
00538 #else
00539                 ewgts[j] = -1.0 / (float) dist_list[j];
00540 #endif
00541                 degree -= ewgts[j];
00542             }
00543         } else {
00544             for (j = 1; j < lap[i].nedges; j++) {
00545                 edges[j] = distances[i].edges[j - 1];
00546                 ewgts[j] = -1.0 / (float) dist_list[j];
00547                 degree -= ewgts[j];
00548             }
00549         }
00550         edges[0] = i;
00551         ewgts[0] = (float) degree;
00552         edges += lap[i].nedges;
00553         ewgts += lap[i].nedges;
00554     }
00555 
00556         /*************************************************
00557         ** initialize direction vectors  **
00558         ** to get an intial layout       **
00559         *************************************************/
00560 
00561     /* the layout is subspace*directions */
00562     directions = N_GNEW(dim, double *);
00563     directions[0] = N_GNEW(dim * subspace_dim, double);
00564     for (i = 1; i < dim; i++) {
00565         directions[i] = directions[0] + i * subspace_dim;
00566     }
00567 
00568     if (smart_ini) {
00569         /* smart initialization */
00570         for (k = 0; k < dim; k++) {
00571             for (i = 0; i < subspace_dim; i++) {
00572                 directions[k][i] = 0;
00573             }
00574         }
00575         if (dim != 2) {
00576             /* use the first vectors in the eigenspace */
00577             /* each direction points to its "principal axes" */
00578             for (k = 0; k < dim; k++) {
00579                 directions[k][k] = 1;
00580             }
00581         } else {
00582             /* for the frequent 2-D case we prefer iterative-PCA over PCA */
00583             /* Note that we don't want to mix the Lap's eigenspace with the HDE */
00584             /* in the computation since they have different scales */
00585 
00586             directions[0][0] = 1;       /* first pca projection vector */
00587             if (!iterativePCA_1D(subspace, subspace_dim, n, directions[1])) {
00588                 for (k = 0; k < subspace_dim; k++) {
00589                     directions[1][k] = 0;
00590                 }
00591                 directions[1][1] = 1;
00592             }
00593         }
00594 
00595     } else {
00596         /* random initialization */
00597         for (k = 0; k < dim; k++) {
00598             for (i = 0; i < subspace_dim; i++) {
00599                 directions[k][i] = (double) (rand()) / RAND_MAX;
00600             }
00601         }
00602     }
00603 
00604     /* compute initial k-D layout */
00605 
00606     for (k = 0; k < dim; k++) {
00607         right_mult_with_vector_transpose(subspace, n, subspace_dim,
00608                                          directions[k], coords[k]);
00609     }
00610 
00611         /*************************************************
00612         ** compute restriction of the laplacian to subspace: ** 
00613         *************************************************/
00614 
00615     tmp_mat = NULL;
00616     matrix = NULL;
00617     mult_sparse_dense_mat_transpose(lap, subspace, n, subspace_dim,
00618                                     &tmp_mat);
00619     mult_dense_mat(subspace, tmp_mat, subspace_dim, n, subspace_dim,
00620                    &matrix);
00621     free(tmp_mat[0]);
00622     free(tmp_mat);
00623 
00624         /*************************************************
00625         ** Layout optimization  **
00626         *************************************************/
00627 
00628     b = N_GNEW(n, double);
00629     b_restricted = N_GNEW(subspace_dim, double);
00630     old_stress = compute_stress1(coords, distances, dim, n, exp);
00631     for (converged = FALSE, iterations = 0;
00632          iterations < n_iterations && !converged; iterations++) {
00633 
00634         /* Axis-by-axis optimization: */
00635         for (k = 0; k < dim; k++) {
00636             /* compute the vector b */
00637             /* multiply on-the-fly with distance-based laplacian */
00638             /* (for saving storage we don't construct this Lap explicitly) */
00639             for (i = 0; i < n; i++) {
00640                 degree = 0;
00641                 b[i] = 0;
00642                 dist_list = distances[i].edist - 1;
00643                 edges = lap[i].edges;
00644                 ewgts = lap[i].ewgts;
00645                 for (j = 1; j < lap[i].nedges; j++) {
00646                     node = edges[j];
00647                     dist_ij = distance_kD(coords, dim, i, node);
00648                     if (dist_ij > 1e-30) {      /* skip zero distances */
00649                         L_ij = -ewgts[j] * dist_list[j] / dist_ij;      /* L_ij=w_{ij}*d_{ij}/dist_{ij} */
00650                         degree -= L_ij;
00651                         b[i] += L_ij * coords[k][node];
00652                     }
00653                 }
00654                 b[i] += degree * coords[k][i];
00655             }
00656             right_mult_with_vector_d(subspace, subspace_dim, n, b,
00657                                      b_restricted);
00658             if (conjugate_gradient_f(matrix, directions[k], b_restricted,
00659                                  subspace_dim, conj_tol, subspace_dim,
00660                                  FALSE)) {
00661                 iterations = -1;
00662                 goto finish0;
00663             }
00664             right_mult_with_vector_transpose(subspace, n, subspace_dim,
00665                                              directions[k], coords[k]);
00666         }
00667 
00668         if ((converged = (iterations % 2 == 0))) {      /* check for convergence each two iterations */
00669             new_stress = compute_stress1(coords, distances, dim, n, exp);
00670             converged =
00671                 fabs(new_stress - old_stress) / (new_stress + 1e-10) <
00672                 Epsilon;
00673             old_stress = new_stress;
00674         }
00675     }
00676 finish0:
00677     free(b_restricted);
00678     free(b);
00679 
00680     if (reweight_graph) {
00681         restore_old_weights(graph, n, old_weights);
00682     }
00683 
00684     for (i = 0; i < n; i++) {
00685         if (distances[i].free_mem) {
00686             free(distances[i].edges);
00687             free(distances[i].edist);
00688         }
00689     }
00690 
00691     free(distances);
00692     free(lap[0].edges);
00693     free(lap[0].ewgts);
00694     free(lap);
00695     free(CenterIndex);
00696     free(invCenterIndex);
00697     free(directions[0]);
00698     free(directions);
00699     if (matrix != NULL) {
00700         free(matrix[0]);
00701         free(matrix);
00702     }
00703     free(subspace[0]);
00704     free(subspace);
00705     freeQueue(&Q);
00706 
00707     return iterations;
00708 }
00709 
00710 /* compute_weighted_apsp_packed:
00711  * Edge lengths can be any float > 0
00712  */
00713 static float *compute_weighted_apsp_packed(vtx_data * graph, int n)
00714 {
00715     int i, j, count;
00716     float *Dij = N_NEW(n * (n + 1) / 2, float);
00717 
00718     float *Di = N_NEW(n, float);
00719     Queue Q;
00720 
00721     mkQueue(&Q, n);
00722 
00723     count = 0;
00724     for (i = 0; i < n; i++) {
00725         dijkstra_f(i, graph, n, Di);
00726         for (j = i; j < n; j++) {
00727             Dij[count++] = Di[j];
00728         }
00729     }
00730     free(Di);
00731     freeQueue(&Q);
00732     return Dij;
00733 }
00734 
00735 
00736 /* mdsModel:
00737  * Update matrix with actual edge lengths
00738  */
00739 float *mdsModel(vtx_data * graph, int nG)
00740 {
00741     int i, j, e;
00742     float *Dij;
00743     int shift = 0;
00744     double delta;
00745 
00746     if (graph->ewgts == NULL)
00747         return 0;
00748 
00749     /* first, compute shortest paths to fill in non-edges */
00750     Dij = compute_weighted_apsp_packed(graph, nG);
00751 
00752     /* then, replace edge entries will user-supplied len */
00753     for (i = 0; i < nG; i++) {
00754         shift += i;
00755         for (e = 1; e < graph[i].nedges; e++) {
00756             j = graph[i].edges[e];
00757             if (j < i)
00758                 continue;
00759             delta += abs(Dij[i * nG + j - shift] - graph[i].ewgts[e]);
00760             Dij[i * nG + j - shift] = graph[i].ewgts[e];
00761         }
00762     }
00763     if (Verbose) {
00764         fprintf(stderr, "mdsModel: delta = %f\n", delta);
00765     }
00766     return Dij;
00767 }
00768 
00769 /* compute_apsp_packed:
00770  * Assumes integral weights > 0.
00771  */
00772 float *compute_apsp_packed(vtx_data * graph, int n)
00773 {
00774     int i, j, count;
00775     float *Dij = N_NEW(n * (n + 1) / 2, float);
00776 
00777     DistType *Di = N_NEW(n, DistType);
00778     Queue Q;
00779 
00780     mkQueue(&Q, n);
00781 
00782     count = 0;
00783     for (i = 0; i < n; i++) {
00784         bfs(i, graph, n, Di, &Q);
00785         for (j = i; j < n; j++) {
00786             Dij[count++] = ((float) Di[j]);
00787         }
00788     }
00789     free(Di);
00790     freeQueue(&Q);
00791     return Dij;
00792 }
00793 
00794 #define max(x,y) ((x)>(y)?(x):(y))
00795 
00796 float *compute_apsp_artifical_weights_packed(vtx_data * graph, int n)
00797 {
00798     /* compute all-pairs-shortest-path-length while weighting the graph */
00799     /* so high-degree nodes are distantly located */
00800 
00801     float *Dij;
00802     int i, j;
00803     float *old_weights = graph[0].ewgts;
00804     int nedges = 0;
00805     float *weights;
00806     int *vtx_vec;
00807     int deg_i, deg_j, neighbor;
00808 
00809     for (i = 0; i < n; i++) {
00810         nedges += graph[i].nedges;
00811     }
00812 
00813     weights = N_NEW(nedges, float);
00814     vtx_vec = N_NEW(n, int);
00815     for (i = 0; i < n; i++) {
00816         vtx_vec[i] = 0;
00817     }
00818 
00819     if (graph->ewgts) {
00820         for (i = 0; i < n; i++) {
00821             fill_neighbors_vec_unweighted(graph, i, vtx_vec);
00822             deg_i = graph[i].nedges - 1;
00823             for (j = 1; j <= deg_i; j++) {
00824                 neighbor = graph[i].edges[j];
00825                 deg_j = graph[neighbor].nedges - 1;
00826                 weights[j] = (float)
00827                     max((float)
00828                         (deg_i + deg_j -
00829                          2 * common_neighbors(graph, i, neighbor,
00830                                               vtx_vec)),
00831                         graph[i].ewgts[j]);
00832             }
00833             empty_neighbors_vec(graph, i, vtx_vec);
00834             graph[i].ewgts = weights;
00835             weights += graph[i].nedges;
00836         }
00837         Dij = compute_weighted_apsp_packed(graph, n);
00838     } else {
00839         for (i = 0; i < n; i++) {
00840             graph[i].ewgts = weights;
00841             fill_neighbors_vec_unweighted(graph, i, vtx_vec);
00842             deg_i = graph[i].nedges - 1;
00843             for (j = 1; j <= deg_i; j++) {
00844                 neighbor = graph[i].edges[j];
00845                 deg_j = graph[neighbor].nedges - 1;
00846                 weights[j] =
00847                     ((float) deg_i + deg_j -
00848                      2 * common_neighbors(graph, i, neighbor, vtx_vec));
00849             }
00850             empty_neighbors_vec(graph, i, vtx_vec);
00851             weights += graph[i].nedges;
00852         }
00853         Dij = compute_apsp_packed(graph, n);
00854     }
00855 
00856     free(vtx_vec);
00857     free(graph[0].ewgts);
00858     graph[0].ewgts = NULL;
00859     if (old_weights != NULL) {
00860         for (i = 0; i < n; i++) {
00861             graph[i].ewgts = old_weights;
00862             old_weights += graph[i].nedges;
00863         }
00864     }
00865     return Dij;
00866 }
00867 
00868 #ifdef DEBUG
00869 static void dumpMatrix(float *Dij, int n)
00870 {
00871     int i, j, count = 0;
00872     for (i = 0; i < n; i++) {
00873         for (j = i; j < n; j++) {
00874             fprintf(stderr, "%.02f  ", Dij[count++]);
00875         }
00876         fputs("\n", stderr);
00877     }
00878 }
00879 #endif
00880 
00881 /* Accumulator type for diagonal of Laplacian. Needs to be as large
00882  * as possible. Use long double; configure to double if necessary.
00883  */
00884 #define DegType long double
00885 
00886 /* stress_majorization_kD_mkernel:
00887  * At present, if any nodes have pos set, smart_ini is false.
00888  */
00889 int stress_majorization_kD_mkernel(vtx_data * graph,    /* Input graph in sparse representation */
00890                                    int n,       /* Number of nodes */
00891                                    int nedges_graph,    /* Number of edges */
00892                                    double **d_coords,   /* coordinates of nodes (output layout) */
00893                                    node_t ** nodes,     /* original nodes */
00894                                    int dim,     /* dimemsionality of layout */
00895                                    int opts,    /* options */
00896                                    int model,   /* model */
00897                                    int maxi     /* max iterations */
00898     )
00899 {
00900     int iterations;             /* output: number of iteration of the process */
00901 
00902     double conj_tol = tolerance_cg;     /* tolerance of Conjugate Gradient */
00903     float *Dij = NULL;
00904     int i, j, k;
00905     float **coords = NULL;
00906     float *f_storage = NULL;
00907     float constant_term;
00908     int count;
00909     DegType degree;
00910     int lap_length;
00911     float *lap2 = NULL;
00912     DegType *degrees = NULL;
00913     int step;
00914     float val;
00915     double old_stress, new_stress;
00916     boolean converged;
00917     float **b = NULL;
00918     float *tmp_coords = NULL;
00919     float *dist_accumulator = NULL;
00920     float *lap1 = NULL;
00921     int smart_ini = opts & opt_smart_init;
00922     int exp = opts & opt_exp_flag;
00923     int len;
00924     int havePinned;             /* some node is pinned */
00925 #ifdef ALTERNATIVE_STRESS_CALC
00926     double mat_stress;
00927 #endif
00928 #ifdef NONCORE
00929     FILE *fp = NULL;
00930 #endif
00931 
00932 
00933         /*************************************************
00934         ** Computation of full, dense, unrestricted k-D ** 
00935         ** stress minimization by majorization          **    
00936         *************************************************/
00937 
00938         /****************************************************
00939         ** Compute the all-pairs-shortest-distances matrix **
00940         ****************************************************/
00941 
00942     if (maxi < 0)
00943         return 0;
00944 
00945     if (Verbose)
00946         start_timer();
00947 
00948     if (model == MODEL_SUBSET) {
00949         /* weight graph to separate high-degree nodes */
00950         /* and perform slower Dijkstra-based computation */
00951         if (Verbose)
00952             fprintf(stderr, "Calculating subset model");
00953         Dij = compute_apsp_artifical_weights_packed(graph, n);
00954     } else if (model == MODEL_CIRCUIT) {
00955         Dij = circuitModel(graph, n);
00956         if (!Dij) {
00957             agerr(AGWARN,
00958                   "graph is disconnected. Hence, the circuit model\n");
00959             agerr(AGPREV,
00960                   "is undefined. Reverting to the shortest path model.\n");
00961         }
00962     } else if (model == MODEL_MDS) {
00963         if (Verbose)
00964             fprintf(stderr, "Calculating MDS model");
00965         Dij = mdsModel(graph, n);
00966     }
00967     if (!Dij) {
00968         if (Verbose)
00969             fprintf(stderr, "Calculating shortest paths");
00970         if (graph->ewgts)
00971             Dij = compute_weighted_apsp_packed(graph, n);
00972         else
00973             Dij = compute_apsp_packed(graph, n);
00974     }
00975 
00976     if (Verbose) {
00977         fprintf(stderr, ": %.2f sec\n", elapsed_sec());
00978         fprintf(stderr, "Setting initial positions");
00979         start_timer();
00980     }
00981 
00982         /**************************
00983         ** Layout initialization **
00984         **************************/
00985 
00986     if (smart_ini && (n > 1)) {
00987         havePinned = 0;
00988         /* optimize layout quickly within subspace */
00989         /* perform at most 50 iterations within 30-D subspace to 
00990            get an estimate */
00991         if (sparse_stress_subspace_majorization_kD(graph, n, nedges_graph,
00992                                                d_coords, dim, smart_ini, exp,
00993                                                (model == MODEL_SUBSET), 50,
00994                                                neighborhood_radius_subspace,
00995                                                num_pivots_stress) < 0) {
00996             iterations = -1;
00997             goto finish1;
00998         }
00999 
01000         for (i = 0; i < dim; i++) {
01001             /* for numerical stability, scale down layout */
01002             double max = 1;
01003             for (j = 0; j < n; j++) {
01004                 if (fabs(d_coords[i][j]) > max) {
01005                     max = fabs(d_coords[i][j]);
01006                 }
01007             }
01008             for (j = 0; j < n; j++) {
01009                 d_coords[i][j] /= max;
01010             }
01011             /* add small random noise */
01012             for (j = 0; j < n; j++) {
01013                 d_coords[i][j] += 1e-6 * (drand48() - 0.5);
01014             }
01015             orthog1(n, d_coords[i]);
01016         }
01017     } else {
01018         havePinned = initLayout(graph, n, dim, d_coords, nodes);
01019     }
01020     if (Verbose)
01021         fprintf(stderr, ": %.2f sec", elapsed_sec());
01022     if ((n == 1) || (maxi == 0))
01023         return 0;
01024 
01025     if (Verbose) {
01026         fprintf(stderr, ": %.2f sec\n", elapsed_sec());
01027         fprintf(stderr, "Setting up stress function");
01028         start_timer();
01029     }
01030     coords = N_NEW(dim, float *);
01031     f_storage = N_NEW(dim * n, float);
01032     for (i = 0; i < dim; i++) {
01033         coords[i] = f_storage + i * n;
01034         for (j = 0; j < n; j++) {
01035             coords[i][j] = ((float) d_coords[i][j]);
01036         }
01037     }
01038 
01039     /* compute constant term in stress sum */
01040     /* which is \sum_{i<j} w_{ij}d_{ij}^2 */
01041     if (exp) {
01042 #ifdef Dij2
01043         constant_term = ((float) n * (n - 1) / 2);
01044 #else
01045         constant_term = 0;
01046         for (count = 0, i = 0; i < n - 1; i++) {
01047             count++;            /* skip self distance */
01048             for (j = 1; j < n - i; j++, count++) {
01049                 constant_term += Dij[count];
01050             }
01051         }
01052 #endif
01053     } else {
01054         constant_term = 0;
01055         for (count = 0, i = 0; i < n - 1; i++) {
01056             count++;            /* skip self distance */
01057             for (j = 1; j < n - i; j++, count++) {
01058                 constant_term += Dij[count];
01059             }
01060         }
01061     }
01062 
01063         /**************************
01064         ** Laplacian computation **
01065         **************************/
01066 
01067     lap_length = n * (n + 1) / 2;
01068     lap2 = Dij;
01069     if (exp == 2) {
01070 #ifdef Dij2
01071     square_vec(lap_length, lap2);
01072 #endif
01073     }
01074     /* compute off-diagonal entries */
01075     invert_vec(lap_length, lap2);
01076 
01077     /* compute diagonal entries */
01078     count = 0;
01079     degrees = N_NEW(n, DegType);
01080     /* set_vector_val(n, 0, degrees); */
01081     memset(degrees, 0, n * sizeof(DegType));
01082     for (i = 0; i < n - 1; i++) {
01083         degree = 0;
01084         count++;                /* skip main diag entry */
01085         for (j = 1; j < n - i; j++, count++) {
01086             val = lap2[count];
01087             degree += val;
01088             degrees[i + j] -= val;
01089         }
01090         degrees[i] -= degree;
01091     }
01092     for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) {
01093         lap2[count] = degrees[i];
01094     }
01095 
01096 #ifdef NONCORE
01097     if (n > max_nodes_in_mem) {
01098 #define FILENAME "tmp_Dij$$$.bin"
01099         fp = fopen(FILENAME, "wb");
01100         fwrite(lap2, sizeof(float), lap_length, fp);
01101         fclose(fp);
01102         fp = NULL;
01103     }
01104 #endif
01105 
01106         /*************************
01107         ** Layout optimization  **
01108         *************************/
01109 
01110     b = N_NEW(dim, float *);
01111     b[0] = N_NEW(dim * n, float);
01112     for (k = 1; k < dim; k++) {
01113         b[k] = b[0] + k * n;
01114     }
01115 
01116     tmp_coords = N_NEW(n, float);
01117     dist_accumulator = N_NEW(n, float);
01118     lap1 = NULL;
01119 #ifdef NONCORE
01120     if (n <= max_nodes_in_mem) {
01121         lap1 = N_NEW(lap_length, float);
01122     } else {
01123         lap1 = lap2;
01124         fp = fopen(FILENAME, "rb");
01125         fgetpos(fp, &pos);
01126     }
01127 #else
01128     lap1 = N_NEW(lap_length, float);
01129 #endif
01130 
01131 
01132 #ifdef USE_MAXFLOAT
01133     old_stress = MAXFLOAT;      /* at least one iteration */
01134 #else
01135     old_stress = MAXDOUBLE;     /* at least one iteration */
01136 #endif
01137     if (Verbose) {
01138         fprintf(stderr, ": %.2f sec\n", elapsed_sec());
01139         fprintf(stderr, "Solving model: ");
01140         start_timer();
01141     }
01142 
01143     for (converged = FALSE, iterations = 0;
01144          iterations < maxi && !converged; iterations++) {
01145 
01146         /* First, construct Laplacian of 1/(d_ij*|p_i-p_j|)  */
01147         /* set_vector_val(n, 0, degrees); */
01148         memset(degrees, 0, n * sizeof(DegType));
01149         if (exp == 2) {
01150 #ifdef Dij2
01151 #ifdef NONCORE
01152             if (n <= max_nodes_in_mem) {
01153                 sqrt_vecf(lap_length, lap2, lap1);
01154             } else {
01155                 sqrt_vec(lap_length, lap1);
01156             }
01157 #else
01158             sqrt_vecf(lap_length, lap2, lap1);
01159 #endif
01160 #endif
01161         }
01162         for (count = 0, i = 0; i < n - 1; i++) {
01163             len = n - i - 1;
01164             /* init 'dist_accumulator' with zeros */
01165             set_vector_valf(len, 0, dist_accumulator);
01166 
01167             /* put into 'dist_accumulator' all squared distances between 'i' and 'i'+1,...,'n'-1 */
01168             for (k = 0; k < dim; k++) {
01169                 set_vector_valf(len, coords[k][i], tmp_coords);
01170                 vectors_mult_additionf(len, tmp_coords, -1,
01171                                        coords[k] + i + 1);
01172                 square_vec(len, tmp_coords);
01173                 vectors_additionf(len, tmp_coords, dist_accumulator,
01174                                   dist_accumulator);
01175             }
01176 
01177             /* convert to 1/d_{ij} */
01178             invert_sqrt_vec(len, dist_accumulator);
01179             /* detect overflows */
01180             for (j = 0; j < len; j++) {
01181                 if (dist_accumulator[j] >= MAXFLOAT
01182                     || dist_accumulator[j] < 0) {
01183                     dist_accumulator[j] = 0;
01184                 }
01185             }
01186 
01187             count++;            /* save place for the main diagonal entry */
01188             degree = 0;
01189             if (exp == 2) {
01190                 for (j = 0; j < len; j++, count++) {
01191 #ifdef Dij2
01192                     val = lap1[count] *= dist_accumulator[j];
01193 #else
01194                     val = lap1[count] = dist_accumulator[j];
01195 #endif
01196                     degree += val;
01197                     degrees[i + j + 1] -= val;
01198                 }
01199             } else {
01200                 for (j = 0; j < len; j++, count++) {
01201                     val = lap1[count] = dist_accumulator[j];
01202                     degree += val;
01203                     degrees[i + j + 1] -= val;
01204                 }
01205             }
01206             degrees[i] -= degree;
01207         }
01208         for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) {
01209             lap1[count] = degrees[i];
01210         }
01211 
01212         /* Now compute b[] */
01213         for (k = 0; k < dim; k++) {
01214             /* b[k] := lap1*coords[k] */
01215             right_mult_with_vector_ff(lap1, n, coords[k], b[k]);
01216         }
01217 
01218 
01219         /* compute new stress  */
01220         /* remember that the Laplacians are negated, so we subtract instead of add and vice versa */
01221         new_stress = 0;
01222         for (k = 0; k < dim; k++) {
01223             new_stress += vectors_inner_productf(n, coords[k], b[k]);
01224         }
01225         new_stress *= 2;
01226         new_stress += constant_term;    /* only after mult by 2 */
01227 #ifdef NONCORE
01228         if (n > max_nodes_in_mem) {
01229             /* restore lap2 from memory */
01230             fsetpos(fp, &pos);
01231             fread(lap2, sizeof(float), lap_length, fp);
01232         }
01233 #endif
01234         for (k = 0; k < dim; k++) {
01235             right_mult_with_vector_ff(lap2, n, coords[k], tmp_coords);
01236             new_stress -= vectors_inner_productf(n, coords[k], tmp_coords);
01237         }
01238 #ifdef ALTERNATIVE_STRESS_CALC
01239         mat_stress = new_stress;
01240         new_stress = compute_stressf(coords, lap2, dim, n);
01241         if (fabs(mat_stress - new_stress) / min(mat_stress, new_stress) >
01242             0.001) {
01243             fprintf(stderr, "Diff stress vals: %lf %lf (iteration #%d)\n",
01244                     mat_stress, new_stress, iterations);
01245         }
01246 #endif
01247         /* Invariant: old_stress > 0. In theory, old_stress >= new_stress
01248          * but we use fabs in case of numerical error.
01249          */
01250         {
01251             double diff = old_stress - new_stress;
01252             double change = ABS(diff);
01253             converged = (((change / old_stress) < Epsilon)
01254                          || (new_stress < Epsilon));
01255         }
01256         old_stress = new_stress;
01257 
01258         for (k = 0; k < dim; k++) {
01259             node_t *np;
01260             if (havePinned) {
01261                 copy_vectorf(n, coords[k], tmp_coords);
01262                 if (conjugate_gradient_mkernel(lap2, tmp_coords, b[k], n,
01263                                            conj_tol, n) < 0) {
01264                     iterations = -1;
01265                     goto finish1;
01266                 }
01267                 for (i = 0; i < n; i++) {
01268                     np = nodes[i];
01269                     if (!isFixed(np))
01270                         coords[k][i] = tmp_coords[i];
01271                 }
01272             } else {
01273                 if (conjugate_gradient_mkernel(lap2, coords[k], b[k], n,
01274                                            conj_tol, n) < 0) {
01275                     iterations = -1;
01276                     goto finish1;
01277                 }
01278             }
01279         }
01280         if (Verbose && (iterations % 5 == 0)) {
01281             fprintf(stderr, "%.3f ", new_stress);
01282             if ((iterations + 5) % 50 == 0)
01283                 fprintf(stderr, "\n");
01284         }
01285     }
01286     if (Verbose) {
01287         fprintf(stderr, "\nfinal e = %f %d iterations %.2f sec\n",
01288                 compute_stressf(coords, lap2, dim, n, exp),
01289                 iterations, elapsed_sec());
01290     }
01291 
01292     for (i = 0; i < dim; i++) {
01293         for (j = 0; j < n; j++) {
01294             d_coords[i][j] = coords[i][j];
01295         }
01296     }
01297 #ifdef NONCORE
01298     if (fp)
01299         fclose(fp);
01300 #endif
01301 finish1:
01302     free(f_storage);
01303     free(coords);
01304 
01305     free(lap2);
01306     if (b) {
01307         free(b[0]);
01308         free(b);
01309     }
01310     free(tmp_coords);
01311     free(dist_accumulator);
01312     free(degrees);
01313     free(lap1);
01314     return iterations;
01315 }