|
Graphviz
2.29.20120523.0446
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00020 /********************************************************** 00021 * Based on constrained_majorization.c 00022 * 00023 * Perform stress majorization subject 00024 * to separation constraints, for background see the paper: 00025 * "IPSep-CoLa: An Incremental Procedure for Separation Constraint Layout of Graphs" 00026 * by Tim Dwyer, Yehuda Koren and Kim Marriott 00027 * 00028 * Available separation constraints so far are: 00029 * o Directed edge constraints 00030 * o Node non-overlap constraints 00031 * o Cluster containment constraints 00032 * o Cluster/node non-overlap constraints 00033 * 00034 * Tim Dwyer, 2006 00035 **********************************************************/ 00036 00037 #include "digcola.h" 00038 #ifdef IPSEPCOLA 00039 #include <math.h> 00040 #include <stdlib.h> 00041 #include <time.h> 00042 #include <stdio.h> 00043 #include <float.h> 00044 #include "stress.h" 00045 #include "dijkstra.h" 00046 #include "bfs.h" 00047 #include "matrix_ops.h" 00048 #include "kkutils.h" 00049 #include "conjgrad.h" 00050 #include <csolve_VPSC.h> 00051 #include "quad_prog_vpsc.h" 00052 #include "quad_prog_solver.h" 00053 #include "matrix_ops.h" 00054 00055 #define localConstrMajorIterations 1000 00056 00057 int stress_majorization_cola(vtx_data * graph, /* Input graph in sparse representation */ 00058 int n, /* Number of nodes */ 00059 int nedges_graph, /* Number of edges */ 00060 double **d_coords, /* Coordinates of nodes (output layout) */ 00061 node_t ** nodes, /* Original nodes */ 00062 int dim, /* Dimemsionality of layout */ 00063 int model, /* difference model */ 00064 int maxi, /* max iterations */ 00065 ipsep_options * opt) 00066 { 00067 int iterations = 0; /* Output: number of iteration of the process */ 00068 00069 /************************************************* 00070 ** Computation of full, dense, unrestricted k-D ** 00071 ** stress minimization by majorization ** 00072 ** This function imposes HIERARCHY CONSTRAINTS ** 00073 *************************************************/ 00074 00075 int i, j, k; 00076 float *lap1 = NULL; 00077 float *dist_accumulator = NULL; 00078 float *tmp_coords = NULL; 00079 float **b = NULL; 00080 double *degrees = NULL; 00081 float *lap2 = NULL; 00082 int lap_length; 00083 float *f_storage = NULL; 00084 float **coords = NULL; 00085 int orig_n = n; 00086 00087 /*double conj_tol=tolerance_cg; *//* tolerance of Conjugate Gradient */ 00088 CMajEnvVPSC *cMajEnvHor = NULL; 00089 CMajEnvVPSC *cMajEnvVrt = NULL; 00090 double y_0; 00091 int length; 00092 DistType diameter; 00093 float *Dij = NULL; 00094 float constant_term; 00095 int count; 00096 double degree; 00097 int step; 00098 float val; 00099 double old_stress, new_stress = 0; 00100 boolean converged; 00101 int len; 00102 double nsizeScale = 0; 00103 float maxEdgeLen = 0; 00104 double max = 1; 00105 00106 initLayout(graph, n, dim, d_coords, nodes); 00107 if (n == 1) 00108 return 0; 00109 00110 for (i = 0; i < n; i++) { 00111 for (j = 1; j < graph[i].nedges; j++) { 00112 maxEdgeLen = MAX(graph[i].ewgts[j], maxEdgeLen); 00113 } 00114 } 00115 00116 /**************************************************** 00117 ** Compute the all-pairs-shortest-distances matrix ** 00118 ****************************************************/ 00119 00120 if (maxi == 0) 00121 return iterations; 00122 00123 if (Verbose) 00124 start_timer(); 00125 00126 if (model == MODEL_SUBSET) { 00127 /* weight graph to separate high-degree nodes */ 00128 /* and perform slower Dijkstra-based computation */ 00129 if (Verbose) 00130 fprintf(stderr, "Calculating subset model"); 00131 Dij = compute_apsp_artifical_weights_packed(graph, n); 00132 } else if (model == MODEL_CIRCUIT) { 00133 Dij = circuitModel(graph, n); 00134 if (!Dij) { 00135 agerr(AGWARN, 00136 "graph is disconnected. Hence, the circuit model\n"); 00137 agerr(AGPREV, 00138 "is undefined. Reverting to the shortest path model.\n"); 00139 } 00140 } else if (model == MODEL_MDS) { 00141 if (Verbose) 00142 fprintf(stderr, "Calculating MDS model"); 00143 Dij = mdsModel(graph, n); 00144 } 00145 if (!Dij) { 00146 if (Verbose) 00147 fprintf(stderr, "Calculating shortest paths"); 00148 Dij = compute_apsp_packed(graph, n); 00149 } 00150 if (Verbose) { 00151 fprintf(stderr, ": %.2f sec\n", elapsed_sec()); 00152 fprintf(stderr, "Setting initial positions"); 00153 start_timer(); 00154 } 00155 00156 diameter = -1; 00157 length = n + n * (n - 1) / 2; 00158 for (i = 0; i < length; i++) { 00159 if (Dij[i] > diameter) { 00160 diameter = (int) Dij[i]; 00161 } 00162 } 00163 00164 /* for numerical stability, scale down layout */ 00165 /* No Jiggling, might conflict with constraints */ 00166 for (i = 0; i < dim; i++) { 00167 for (j = 0; j < n; j++) { 00168 if (fabs(d_coords[i][j]) > max) { 00169 max = fabs(d_coords[i][j]); 00170 } 00171 } 00172 } 00173 for (i = 0; i < dim; i++) { 00174 for (j = 0; j < n; j++) { 00175 d_coords[i][j] *= 10 / max; 00176 } 00177 } 00178 00179 /************************** 00180 ** Layout initialization ** 00181 **************************/ 00182 00183 for (i = 0; i < dim; i++) { 00184 orthog1(n, d_coords[i]); 00185 } 00186 00187 /* for the y-coords, don't center them, but translate them so y[0]=0 */ 00188 y_0 = d_coords[1][0]; 00189 for (i = 0; i < n; i++) { 00190 d_coords[1][i] -= y_0; 00191 } 00192 if (Verbose) 00193 fprintf(stderr, ": %.2f sec", elapsed_sec()); 00194 00195 /************************** 00196 ** Laplacian computation ** 00197 **************************/ 00198 00199 lap2 = Dij; 00200 lap_length = n + n * (n - 1) / 2; 00201 square_vec(lap_length, lap2); 00202 /* compute off-diagonal entries */ 00203 invert_vec(lap_length, lap2); 00204 00205 if (opt->clusters->nclusters > 0) { 00206 int nn = n + opt->clusters->nclusters * 2; 00207 int clap_length = nn + nn * (nn - 1) / 2; 00208 float *clap = N_GNEW(clap_length, float); 00209 int c0, c1; 00210 float v; 00211 c0 = c1 = 0; 00212 for (i = 0; i < nn; i++) { 00213 for (j = 0; j < nn - i; j++) { 00214 if (i < n && j < n - i) { 00215 v = lap2[c0++]; 00216 } else { 00217 /* v=j==1?i%2:0; */ 00218 if (j == 1 && i % 2 == 1) { 00219 v = maxEdgeLen; 00220 v *= v; 00221 if (v > 0.01) { 00222 v = 1.0 / v; 00223 } 00224 } else 00225 v = 0; 00226 } 00227 clap[c1++] = v; 00228 } 00229 } 00230 free(lap2); 00231 lap2 = clap; 00232 n = nn; 00233 lap_length = clap_length; 00234 } 00235 /* compute diagonal entries */ 00236 count = 0; 00237 degrees = N_GNEW(n, double); 00238 set_vector_val(n, 0, degrees); 00239 for (i = 0; i < n - 1; i++) { 00240 degree = 0; 00241 count++; /* skip main diag entry */ 00242 for (j = 1; j < n - i; j++, count++) { 00243 val = lap2[count]; 00244 degree += val; 00245 degrees[i + j] -= val; 00246 } 00247 degrees[i] -= degree; 00248 } 00249 for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) { 00250 lap2[count] = (float) degrees[i]; 00251 } 00252 00253 coords = N_GNEW(dim, float *); 00254 f_storage = N_GNEW(dim * n, float); 00255 for (i = 0; i < dim; i++) { 00256 coords[i] = f_storage + i * n; 00257 for (j = 0; j < n; j++) { 00258 coords[i][j] = j < orig_n ? (float) (d_coords[i][j]) : 0; 00259 } 00260 } 00261 00262 /* compute constant term in stress sum 00263 * which is \sum_{i<j} w_{ij}d_{ij}^2 00264 */ 00265 constant_term = (float) (n * (n - 1) / 2); 00266 00267 /************************* 00268 ** Layout optimization ** 00269 *************************/ 00270 00271 b = N_GNEW(dim, float *); 00272 b[0] = N_GNEW(dim * n, float); 00273 for (k = 1; k < dim; k++) { 00274 b[k] = b[0] + k * n; 00275 } 00276 00277 tmp_coords = N_GNEW(n, float); 00278 dist_accumulator = N_GNEW(n, float); 00279 00280 old_stress = DBL_MAX; /* at least one iteration */ 00281 00282 if ((cMajEnvHor = initCMajVPSC(n, lap2, graph, opt, 0)) == NULL) { 00283 iterations = -1; 00284 goto finish; 00285 } 00286 if ((cMajEnvVrt = initCMajVPSC(n, lap2, graph, opt, opt->diredges)) == NULL) { 00287 iterations = -1; 00288 goto finish; 00289 } 00290 00291 lap1 = N_GNEW(lap_length, float); 00292 00293 for (converged = FALSE, iterations = 0; 00294 iterations < maxi && !converged; iterations++) { 00295 00296 /* First, construct Laplacian of 1/(d_ij*|p_i-p_j|) */ 00297 set_vector_val(n, 0, degrees); 00298 sqrt_vecf(lap_length, lap2, lap1); 00299 for (count = 0, i = 0; i < n - 1; i++) { 00300 len = n - i - 1; 00301 /* init 'dist_accumulator' with zeros */ 00302 set_vector_valf(n, 0, dist_accumulator); 00303 00304 /* put into 'dist_accumulator' all squared distances 00305 * between 'i' and 'i'+1,...,'n'-1 00306 */ 00307 for (k = 0; k < dim; k++) { 00308 set_vector_valf(len, coords[k][i], tmp_coords); 00309 vectors_mult_additionf(len, tmp_coords, -1, 00310 coords[k] + i + 1); 00311 square_vec(len, tmp_coords); 00312 vectors_additionf(len, tmp_coords, dist_accumulator, 00313 dist_accumulator); 00314 } 00315 00316 /* convert to 1/d_{ij} */ 00317 invert_sqrt_vec(len, dist_accumulator); 00318 /* detect overflows */ 00319 for (j = 0; j < len; j++) { 00320 if (dist_accumulator[j] >= FLT_MAX 00321 || dist_accumulator[j] < 0) { 00322 dist_accumulator[j] = 0; 00323 } 00324 } 00325 00326 count++; /* save place for the main diagonal entry */ 00327 degree = 0; 00328 for (j = 0; j < len; j++, count++) { 00329 val = lap1[count] *= dist_accumulator[j]; 00330 degree += val; 00331 degrees[i + j + 1] -= val; 00332 } 00333 degrees[i] -= degree; 00334 } 00335 for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) { 00336 lap1[count] = (float) degrees[i]; 00337 } 00338 00339 /* Now compute b[] (L^(X(t))*X(t)) */ 00340 for (k = 0; k < dim; k++) { 00341 /* b[k] := lap1*coords[k] */ 00342 right_mult_with_vector_ff(lap1, n, coords[k], b[k]); 00343 } 00344 00345 /* compute new stress 00346 * remember that the Laplacians are negated, so we subtract 00347 * instead of add and vice versa 00348 */ 00349 new_stress = 0; 00350 for (k = 0; k < dim; k++) { 00351 new_stress += vectors_inner_productf(n, coords[k], b[k]); 00352 } 00353 new_stress *= 2; 00354 new_stress += constant_term; /* only after mult by 2 */ 00355 for (k = 0; k < dim; k++) { 00356 right_mult_with_vector_ff(lap2, n, coords[k], tmp_coords); 00357 new_stress -= vectors_inner_productf(n, coords[k], tmp_coords); 00358 } 00359 00360 #ifdef ALTERNATIVE_STRESS_CALC 00361 { 00362 double mat_stress = new_stress; 00363 double compute_stress(float **coords, float *lap, int dim, 00364 int n); 00365 new_stress = compute_stress(coords, lap2, dim, n); 00366 if (fabs(mat_stress - new_stress) / 00367 min(mat_stress, new_stress) > 0.001) { 00368 fprintf(stderr, 00369 "Diff stress vals: %lf %lf (iteration #%d)\n", 00370 mat_stress, new_stress, iterations); 00371 } 00372 } 00373 #endif 00374 /* check for convergence */ 00375 if (Verbose && (iterations % 1 == 0)) { 00376 fprintf(stderr, "%.3f ", new_stress); 00377 if (iterations % 10 == 0) 00378 fprintf(stderr, "\n"); 00379 } 00380 converged = new_stress < old_stress 00381 && fabs(new_stress - old_stress) / fabs(old_stress + 1e-10) < 00382 Epsilon; 00383 /*converged = converged || (iterations>1 && new_stress>old_stress); */ 00384 /* in first iteration we allowed stress increase, which 00385 * might result ny imposing constraints 00386 */ 00387 old_stress = new_stress; 00388 00389 /* in determining non-overlap constraints we gradually scale up the 00390 * size of nodes to avoid local minima 00391 */ 00392 if ((iterations >= maxi - 1 || converged) && opt->noverlap == 1 00393 && nsizeScale < 0.999) { 00394 nsizeScale += 0.1; 00395 if (Verbose) 00396 fprintf(stderr, "nsizescale=%f,iterations=%d\n", 00397 nsizeScale, iterations); 00398 iterations = 0; 00399 converged = FALSE; 00400 } 00401 00402 00403 /* now we find the optimizer of trace(X'LX)+X'B by solving 'dim' 00404 * system of equations, thereby obtaining the new coordinates. 00405 * If we use the constraints (given by the var's: 'ordering', 00406 * 'levels' and 'num_levels'), we cannot optimize 00407 * trace(X'LX)+X'B by simply solving equations, but we have 00408 * to use a quadratic programming solver 00409 * note: 'lap2' is a packed symmetric matrix, that is its 00410 * upper-triangular part is arranged in a vector row-wise 00411 * also note: 'lap2' is really the negated laplacian (the 00412 * laplacian is -'lap2') 00413 */ 00414 00415 if (opt->noverlap == 1 && nsizeScale > 0.001) { 00416 generateNonoverlapConstraints(cMajEnvHor, nsizeScale, coords, 00417 0, 00418 nsizeScale < 0.5 ? FALSE : TRUE, 00419 opt); 00420 } 00421 if (cMajEnvHor->m > 0) { 00422 #ifdef MOSEK 00423 if (opt->mosek) { 00424 mosek_quad_solve_sep(cMajEnvHor->mosekEnv, n, b[0], 00425 coords[0]); 00426 } else 00427 #endif /* MOSEK */ 00428 constrained_majorization_vpsc(cMajEnvHor, b[0], coords[0], 00429 localConstrMajorIterations); 00430 } else { 00431 /* if there are no constraints then use conjugate gradient 00432 * optimisation which should be considerably faster 00433 */ 00434 if (conjugate_gradient_mkernel(lap2, coords[0], b[0], n, 00435 tolerance_cg, n) < 0) { 00436 iterations = -1; 00437 goto finish; 00438 } 00439 } 00440 if (opt->noverlap == 1 && nsizeScale > 0.001) { 00441 generateNonoverlapConstraints(cMajEnvVrt, nsizeScale, coords, 00442 1, FALSE, opt); 00443 } 00444 if (cMajEnvVrt->m > 0) { 00445 #ifdef MOSEK 00446 if (opt->mosek) { 00447 mosek_quad_solve_sep(cMajEnvVrt->mosekEnv, n, b[1], 00448 coords[1]); 00449 } else 00450 #endif /* MOSEK */ 00451 if (constrained_majorization_vpsc(cMajEnvVrt, b[1], coords[1], 00452 localConstrMajorIterations) < 0) { 00453 iterations = -1; 00454 goto finish; 00455 } 00456 } else { 00457 conjugate_gradient_mkernel(lap2, coords[1], b[1], n, 00458 tolerance_cg, n); 00459 } 00460 } 00461 if (Verbose) { 00462 fprintf(stderr, "\nfinal e = %f %d iterations %.2f sec\n", 00463 new_stress, iterations, elapsed_sec()); 00464 } 00465 deleteCMajEnvVPSC(cMajEnvHor); 00466 deleteCMajEnvVPSC(cMajEnvVrt); 00467 00468 if (opt->noverlap == 2) { 00469 /* fprintf(stderr, "Removing overlaps as post-process...\n"); */ 00470 removeoverlaps(orig_n, coords, opt); 00471 } 00472 00473 finish: 00474 if (coords != NULL) { 00475 for (i = 0; i < dim; i++) { 00476 for (j = 0; j < orig_n; j++) { 00477 d_coords[i][j] = coords[i][j]; 00478 } 00479 } 00480 free(coords[0]); 00481 free(coords); 00482 } 00483 00484 if (b) { 00485 free(b[0]); 00486 free(b); 00487 } 00488 free(tmp_coords); 00489 free(dist_accumulator); 00490 free(degrees); 00491 free(lap2); 00492 free(lap1); 00493 00494 return iterations; 00495 } 00496 #endif /* IPSEPCOLA */
1.7.5