|
Graphviz
2.31.20130618.0446
|
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 }
1.7.5