| 1 | /* $Id$ $Revision$ */ |
| 2 | /* vim:set shiftwidth=4 ts=8: */ |
| 3 | |
| 4 | /************************************************************************* |
| 5 | * Copyright (c) 2011 AT&T Intellectual Property |
| 6 | * All rights reserved. This program and the accompanying materials |
| 7 | * are made available under the terms of the Eclipse Public License v1.0 |
| 8 | * which accompanies this distribution, and is available at |
| 9 | * http://www.eclipse.org/legal/epl-v10.html |
| 10 | * |
| 11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
| 12 | *************************************************************************/ |
| 13 | |
| 14 | #include "config.h" |
| 15 | |
| 16 | #include <time.h> |
| 17 | #include <string.h> |
| 18 | #include <math.h> |
| 19 | |
| 20 | #include "types.h" |
| 21 | #include "memory.h" |
| 22 | #include "globals.h" |
| 23 | |
| 24 | #include "sparse_solve.h" |
| 25 | #include "post_process.h" |
| 26 | #include "overlap.h" |
| 27 | #include "spring_electrical.h" |
| 28 | #include "call_tri.h" |
| 29 | #include "sfdpinternal.h" |
| 30 | |
| 31 | #define node_degree(i) (ia[(i)+1] - ia[(i)]) |
| 32 | |
| 33 | #ifdef UNUSED |
| 34 | static void get_neighborhood_precision_recall(char *outfile, SparseMatrix A0, real *ideal_dist_matrix, real *dist_matrix){ |
| 35 | SparseMatrix A = A0; |
| 36 | int i, j, k, n = A->m; |
| 37 | // int *ia, *ja; |
| 38 | int *g_order = NULL, *p_order = NULL;/* ordering using graph/physical distance */ |
| 39 | real *gdist, *pdist, radius; |
| 40 | int np_neighbors; |
| 41 | int ng_neighbors; /*number of (graph theoretical) neighbors */ |
| 42 | real node_dist;/* distance of a node to the center node */ |
| 43 | real true_positive; |
| 44 | real recall; |
| 45 | FILE *fp; |
| 46 | |
| 47 | fp = fopen(outfile,"w" ); |
| 48 | |
| 49 | if (!SparseMatrix_is_symmetric(A, FALSE)){ |
| 50 | A = SparseMatrix_symmetrize(A, FALSE); |
| 51 | } |
| 52 | // ia = A->ia; |
| 53 | // ja = A->ja; |
| 54 | |
| 55 | for (k = 5; k <= 50; k+= 5){ |
| 56 | recall = 0; |
| 57 | for (i = 0; i < n; i++){ |
| 58 | gdist = &(ideal_dist_matrix[i*n]); |
| 59 | vector_ordering(n, gdist, &g_order, TRUE); |
| 60 | pdist = &(dist_matrix[i*n]); |
| 61 | vector_ordering(n, pdist, &p_order, TRUE); |
| 62 | ng_neighbors = MIN(n-1, k); /* set the number of closest neighbor in the graph space to consider, excluding the node itself */ |
| 63 | np_neighbors = ng_neighbors;/* set the number of closest neighbor in the embedding to consider, excluding the node itself */ |
| 64 | radius = pdist[p_order[np_neighbors]]; |
| 65 | true_positive = 0; |
| 66 | for (j = 1; j <= ng_neighbors; j++){ |
| 67 | node_dist = pdist[g_order[j]];/* the phisical distance for j-th closest node (in graph space) */ |
| 68 | if (node_dist <= radius) true_positive++; |
| 69 | } |
| 70 | recall += true_positive/np_neighbors; |
| 71 | } |
| 72 | recall /= n; |
| 73 | |
| 74 | fprintf(fp,"%d %f\n" , k, recall); |
| 75 | } |
| 76 | fprintf(stderr,"wrote precision/recall in file %s\n" , outfile); |
| 77 | fclose(fp); |
| 78 | |
| 79 | if (A != A0) SparseMatrix_delete(A); |
| 80 | FREE(g_order); FREE(p_order); |
| 81 | } |
| 82 | |
| 83 | |
| 84 | |
| 85 | void dump_distance_edge_length(char *outfile, SparseMatrix A, int dim, real *x){ |
| 86 | int weighted = TRUE; |
| 87 | int n, i, j, nzz; |
| 88 | real *dist_matrix = NULL; |
| 89 | int flag; |
| 90 | real *dij, *xij, wij, top = 0, bot = 0, t; |
| 91 | |
| 92 | int *p = NULL; |
| 93 | real dmin, dmax, xmax, xmin, bsta, bwidth, *xbin, x25, x75, median; |
| 94 | int nbins = 30, nsta, nz = 0; |
| 95 | FILE *fp; |
| 96 | |
| 97 | fp = fopen(outfile,"w" ); |
| 98 | |
| 99 | flag = SparseMatrix_distance_matrix(A, weighted, &dist_matrix); |
| 100 | assert(!flag); |
| 101 | |
| 102 | n = A->m; |
| 103 | dij = MALLOC(sizeof(real)*(n*(n-1)/2)); |
| 104 | xij = MALLOC(sizeof(real)*(n*(n-1)/2)); |
| 105 | for (i = 0; i < n; i++){ |
| 106 | for (j = i+1; j < n; j++){ |
| 107 | dij[nz] = dist_matrix[i*n+j]; |
| 108 | xij[nz] = distance(x, dim, i, j); |
| 109 | if (dij[nz] > 0){ |
| 110 | wij = 1/(dij[nz]*dij[nz]); |
| 111 | } else { |
| 112 | wij = 1; |
| 113 | } |
| 114 | top += wij * dij[nz] * xij[nz]; |
| 115 | bot += wij*xij[nz]*xij[nz]; |
| 116 | nz++; |
| 117 | } |
| 118 | } |
| 119 | if (bot > 0){ |
| 120 | t = top/bot; |
| 121 | } else { |
| 122 | t = 1; |
| 123 | } |
| 124 | fprintf(stderr,"scaling factor = %f\n" , t); |
| 125 | |
| 126 | for (i = 0; i < nz; i++) xij[i] *= t; |
| 127 | |
| 128 | vector_ordering(nz, dij, &p, TRUE); |
| 129 | dmin = dij[p[0]]; |
| 130 | dmax = dij[p[nz-1]]; |
| 131 | nbins = MIN(nbins, dmax/MAX(1,dmin));/* scale by dmin since edge length of 1 is treated as 72 points in stress/maxent/full_stress */ |
| 132 | bwidth = (dmax - dmin)/nbins; |
| 133 | |
| 134 | nsta = 0; bsta = dmin; |
| 135 | xbin = MALLOC(sizeof(real)*nz); |
| 136 | nzz = nz; |
| 137 | |
| 138 | for (i = 0; i < nbins; i++){ |
| 139 | /* the bin is [dmin + i*(dmax-dmin)/nbins, dmin + (i+1)*(dmax-dmin)/nbins] */ |
| 140 | nz = 0; xmin = xmax = xij[p[nsta]]; |
| 141 | while (nsta < nzz && dij[p[nsta]] >= bsta && dij[p[nsta]] <= bsta + bwidth){ |
| 142 | xbin[nz++] = xij[p[nsta]]; |
| 143 | xmin = MIN(xmin, xij[p[nsta]]); |
| 144 | xmax = MAX(xmax, xij[p[nsta]]); |
| 145 | nsta++; |
| 146 | } |
| 147 | /* find the median, and 25/75% */ |
| 148 | if (nz > 0){ |
| 149 | median = vector_median(nz, xbin); |
| 150 | x25 = vector_percentile(nz, xbin, 0.25); |
| 151 | x75 = vector_percentile(nz, xbin, 0.75); |
| 152 | fprintf(fp, "%d %f %f %f %f %f %f %f\n" , nz, bsta, bsta + bwidth, xmin, x25, median, x75, xmax); |
| 153 | } else { |
| 154 | xmin = xmax = median = x25 = x75 = (bsta + 0.5*bwidth); |
| 155 | } |
| 156 | bsta += bwidth; |
| 157 | } |
| 158 | |
| 159 | FREE(dij); FREE(xij); FREE(xbin); FREE(p); |
| 160 | FREE(dist_matrix); |
| 161 | |
| 162 | } |
| 163 | |
| 164 | real get_full_stress(SparseMatrix A, int dim, real *x, int weighting_scheme){ |
| 165 | /* get the full weighted stress, \sum_ij w_ij (t ||x_i-x_j||^2-d_ij)^2, where t |
| 166 | is the optimal scaling factor t = \sum w_ij ||x_i-x_j||^2/(\sum w_ij d_ij ||x_i - x_j||), |
| 167 | |
| 168 | Matrix A is assume to be sparse and a full distance matrix will be generated. |
| 169 | We assume undirected graph and only check an undirected edge i--j, not i->j and j->i. |
| 170 | */ |
| 171 | int weighted = TRUE; |
| 172 | int n, i, j; |
| 173 | real *ideal_dist_matrix = NULL, *dist_matrix; |
| 174 | int flag; |
| 175 | real t, top = 0, bot = 0, lstress, stress = 0, dij, wij, xij; |
| 176 | real sne_disimilarity = 0; |
| 177 | |
| 178 | flag = SparseMatrix_distance_matrix(A, weighted, &ideal_dist_matrix); |
| 179 | assert(!flag); |
| 180 | |
| 181 | n = A->m; |
| 182 | dist_matrix = MALLOC(sizeof(real)*n*n); |
| 183 | |
| 184 | for (i = 0; i < n; i++){ |
| 185 | for (j = i+1; j < n; j++){ |
| 186 | dij = ideal_dist_matrix[i*n+j]; |
| 187 | assert(dij >= 0); |
| 188 | xij = distance(x, dim, i, j); |
| 189 | if (dij > 0){ |
| 190 | switch (weighting_scheme){ |
| 191 | case WEIGHTING_SCHEME_SQR_DIST: |
| 192 | wij = 1/(dij*dij); |
| 193 | break; |
| 194 | case WEIGHTING_SCHEME_INV_DIST: |
| 195 | wij = 1/(dij); |
| 196 | break; |
| 197 | break; |
| 198 | case WEIGHTING_SCHEME_NONE: |
| 199 | wij = 1; |
| 200 | default: |
| 201 | assert(0); |
| 202 | } |
| 203 | } else { |
| 204 | wij = 1; |
| 205 | } |
| 206 | top += wij * dij * xij; |
| 207 | bot += wij*xij*xij; |
| 208 | } |
| 209 | } |
| 210 | if (bot > 0){ |
| 211 | t = top/bot; |
| 212 | } else { |
| 213 | t = 1; |
| 214 | } |
| 215 | |
| 216 | for (i = 0; i < n; i++){ |
| 217 | dist_matrix[i*n+i] = 0.; |
| 218 | for (j = i+1; j < n; j++){ |
| 219 | dij = ideal_dist_matrix[i*n+j]; |
| 220 | assert(dij >= 0); |
| 221 | xij = distance(x, dim, i, j); |
| 222 | dist_matrix[i*n+j] = xij*t; |
| 223 | dist_matrix[j*n+i] = xij*t; |
| 224 | if (dij > 0){ |
| 225 | wij = 1/(dij*dij); |
| 226 | } else { |
| 227 | wij = 1; |
| 228 | } |
| 229 | lstress = (xij*t - dij); |
| 230 | stress += wij*lstress*lstress; |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | {int K = 20; |
| 235 | sne_disimilarity = get_sne_disimilarity(n, ideal_dist_matrix, dist_matrix, K); |
| 236 | } |
| 237 | |
| 238 | get_neighborhood_precision_recall("/tmp/recall.txt" , A, ideal_dist_matrix, dist_matrix); |
| 239 | get_neighborhood_precision_recall("/tmp/precision.txt" , A, dist_matrix, ideal_dist_matrix); |
| 240 | |
| 241 | fprintf(stderr,"sne_disimilarity = %f\n" ,sne_disimilarity); |
| 242 | if (n > 0) fprintf(stderr,"sstress per edge = %f\n" ,stress/n/(n-1)*2); |
| 243 | |
| 244 | FREE(dist_matrix); |
| 245 | FREE(ideal_dist_matrix); |
| 246 | return stress; |
| 247 | } |
| 248 | #endif |
| 249 | |
| 250 | |
| 251 | SparseMatrix ideal_distance_matrix(SparseMatrix A, int dim, real *x){ |
| 252 | /* find the ideal distance between edges, either 1, or |N[i] \Union N[j]| - |N[i] \Intersection N[j]| |
| 253 | */ |
| 254 | SparseMatrix D; |
| 255 | int *ia, *ja, i, j, k, l, nz; |
| 256 | real *d; |
| 257 | int *mask = NULL; |
| 258 | real len, di, sum, sumd; |
| 259 | |
| 260 | assert(SparseMatrix_is_symmetric(A, FALSE)); |
| 261 | |
| 262 | D = SparseMatrix_copy(A); |
| 263 | ia = D->ia; |
| 264 | ja = D->ja; |
| 265 | if (D->type != MATRIX_TYPE_REAL){ |
| 266 | FREE(D->a); |
| 267 | D->type = MATRIX_TYPE_REAL; |
| 268 | D->a = N_GNEW(D->nz,real); |
| 269 | } |
| 270 | d = (real*) D->a; |
| 271 | |
| 272 | mask = N_GNEW(D->m,int); |
| 273 | for (i = 0; i < D->m; i++) mask[i] = -1; |
| 274 | |
| 275 | for (i = 0; i < D->m; i++){ |
| 276 | di = node_degree(i); |
| 277 | mask[i] = i; |
| 278 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 279 | if (i == ja[j]) continue; |
| 280 | mask[ja[j]] = i; |
| 281 | } |
| 282 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 283 | k = ja[j]; |
| 284 | if (i == k) continue; |
| 285 | len = di + node_degree(k); |
| 286 | for (l = ia[k]; l < ia[k+1]; l++){ |
| 287 | if (mask[ja[l]] == i) len--; |
| 288 | } |
| 289 | d[j] = len; |
| 290 | assert(len > 0); |
| 291 | } |
| 292 | |
| 293 | } |
| 294 | |
| 295 | sum = 0; sumd = 0; |
| 296 | nz = 0; |
| 297 | for (i = 0; i < D->m; i++){ |
| 298 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 299 | if (i == ja[j]) continue; |
| 300 | nz++; |
| 301 | sum += distance(x, dim, i, ja[j]); |
| 302 | sumd += d[j]; |
| 303 | } |
| 304 | } |
| 305 | sum /= nz; sumd /= nz; |
| 306 | sum = sum/sumd; |
| 307 | |
| 308 | for (i = 0; i < D->m; i++){ |
| 309 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 310 | if (i == ja[j]) continue; |
| 311 | d[j] = sum*d[j]; |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | |
| 316 | return D; |
| 317 | } |
| 318 | |
| 319 | |
| 320 | StressMajorizationSmoother StressMajorizationSmoother2_new(SparseMatrix A, int dim, real lambda0, real *x, |
| 321 | int ideal_dist_scheme){ |
| 322 | /* use up to dist 2 neighbor */ |
| 323 | /* use up to dist 2 neighbor. This is used in overcoming pherical effect with ideal distance of |
| 324 | 2-neighbors equal graph distance etc. |
| 325 | */ |
| 326 | StressMajorizationSmoother sm; |
| 327 | int i, j, k, l, m = A->m, *ia = A->ia, *ja = A->ja, *iw, *jw, *id, *jd; |
| 328 | int *mask, nz; |
| 329 | real *d, *w, *lambda; |
| 330 | real *avg_dist, diag_d, diag_w, dist, s = 0, stop = 0, sbot = 0; |
| 331 | SparseMatrix ID; |
| 332 | |
| 333 | assert(SparseMatrix_is_symmetric(A, FALSE)); |
| 334 | |
| 335 | ID = ideal_distance_matrix(A, dim, x); |
| 336 | |
| 337 | sm = GNEW(struct StressMajorizationSmoother_struct); |
| 338 | sm->scaling = 1.; |
| 339 | sm->data = NULL; |
| 340 | sm->scheme = SM_SCHEME_NORMAL; |
| 341 | sm->tol_cg = 0.01; |
| 342 | sm->maxit_cg = (int)sqrt((double) A->m); |
| 343 | |
| 344 | lambda = sm->lambda = N_GNEW(m,real); |
| 345 | for (i = 0; i < m; i++) sm->lambda[i] = lambda0; |
| 346 | mask = N_GNEW(m,int); |
| 347 | |
| 348 | avg_dist = N_GNEW(m,real); |
| 349 | |
| 350 | for (i = 0; i < m ;i++){ |
| 351 | avg_dist[i] = 0; |
| 352 | nz = 0; |
| 353 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 354 | if (i == ja[j]) continue; |
| 355 | avg_dist[i] += distance(x, dim, i, ja[j]); |
| 356 | nz++; |
| 357 | } |
| 358 | assert(nz > 0); |
| 359 | avg_dist[i] /= nz; |
| 360 | } |
| 361 | |
| 362 | |
| 363 | for (i = 0; i < m; i++) mask[i] = -1; |
| 364 | |
| 365 | nz = 0; |
| 366 | for (i = 0; i < m; i++){ |
| 367 | mask[i] = i; |
| 368 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 369 | k = ja[j]; |
| 370 | if (mask[k] != i){ |
| 371 | mask[k] = i; |
| 372 | nz++; |
| 373 | } |
| 374 | } |
| 375 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 376 | k = ja[j]; |
| 377 | for (l = ia[k]; l < ia[k+1]; l++){ |
| 378 | if (mask[ja[l]] != i){ |
| 379 | mask[ja[l]] = i; |
| 380 | nz++; |
| 381 | } |
| 382 | } |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | sm->Lw = SparseMatrix_new(m, m, nz + m, MATRIX_TYPE_REAL, FORMAT_CSR); |
| 387 | sm->Lwd = SparseMatrix_new(m, m, nz + m, MATRIX_TYPE_REAL, FORMAT_CSR); |
| 388 | if (!(sm->Lw) || !(sm->Lwd)) { |
| 389 | StressMajorizationSmoother_delete(sm); |
| 390 | return NULL; |
| 391 | } |
| 392 | |
| 393 | iw = sm->Lw->ia; jw = sm->Lw->ja; |
| 394 | |
| 395 | w = (real*) sm->Lw->a; d = (real*) sm->Lwd->a; |
| 396 | |
| 397 | id = sm->Lwd->ia; jd = sm->Lwd->ja; |
| 398 | iw[0] = id[0] = 0; |
| 399 | |
| 400 | nz = 0; |
| 401 | for (i = 0; i < m; i++){ |
| 402 | mask[i] = i+m; |
| 403 | diag_d = diag_w = 0; |
| 404 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 405 | k = ja[j]; |
| 406 | if (mask[k] != i+m){ |
| 407 | mask[k] = i+m; |
| 408 | |
| 409 | jw[nz] = k; |
| 410 | if (ideal_dist_scheme == IDEAL_GRAPH_DIST){ |
| 411 | dist = 1; |
| 412 | } else if (ideal_dist_scheme == IDEAL_AVG_DIST){ |
| 413 | dist = (avg_dist[i] + avg_dist[k])*0.5; |
| 414 | } else if (ideal_dist_scheme == IDEAL_POWER_DIST){ |
| 415 | dist = pow(distance_cropped(x,dim,i,k),.4); |
| 416 | } else { |
| 417 | fprintf(stderr,"ideal_dist_scheme value wrong" ); |
| 418 | assert(0); |
| 419 | exit(1); |
| 420 | } |
| 421 | |
| 422 | /* |
| 423 | w[nz] = -1./(ia[i+1]-ia[i]+ia[ja[j]+1]-ia[ja[j]]); |
| 424 | w[nz] = -2./(avg_dist[i]+avg_dist[k]);*/ |
| 425 | /* w[nz] = -1.;*//* use unit weight for now, later can try 1/(deg(i)+deg(k)) */ |
| 426 | w[nz] = -1/(dist*dist); |
| 427 | |
| 428 | diag_w += w[nz]; |
| 429 | |
| 430 | jd[nz] = k; |
| 431 | /* |
| 432 | d[nz] = w[nz]*distance(x,dim,i,k); |
| 433 | d[nz] = w[nz]*dd[j]; |
| 434 | d[nz] = w[nz]*(avg_dist[i] + avg_dist[k])*0.5; |
| 435 | */ |
| 436 | d[nz] = w[nz]*dist; |
| 437 | stop += d[nz]*distance(x,dim,i,k); |
| 438 | sbot += d[nz]*dist; |
| 439 | diag_d += d[nz]; |
| 440 | |
| 441 | nz++; |
| 442 | } |
| 443 | } |
| 444 | |
| 445 | /* distance 2 neighbors */ |
| 446 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 447 | k = ja[j]; |
| 448 | for (l = ia[k]; l < ia[k+1]; l++){ |
| 449 | if (mask[ja[l]] != i+m){ |
| 450 | mask[ja[l]] = i+m; |
| 451 | |
| 452 | if (ideal_dist_scheme == IDEAL_GRAPH_DIST){ |
| 453 | dist = 2; |
| 454 | } else if (ideal_dist_scheme == IDEAL_AVG_DIST){ |
| 455 | dist = (avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]])*0.5; |
| 456 | } else if (ideal_dist_scheme == IDEAL_POWER_DIST){ |
| 457 | dist = pow(distance_cropped(x,dim,i,ja[l]),.4); |
| 458 | } else { |
| 459 | fprintf(stderr,"ideal_dist_scheme value wrong" ); |
| 460 | assert(0); |
| 461 | exit(1); |
| 462 | } |
| 463 | |
| 464 | jw[nz] = ja[l]; |
| 465 | /* |
| 466 | w[nz] = -1/(ia[i+1]-ia[i]+ia[ja[l]+1]-ia[ja[l]]); |
| 467 | w[nz] = -2/(avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]]);*/ |
| 468 | /* w[nz] = -1.;*//* use unit weight for now, later can try 1/(deg(i)+deg(k)) */ |
| 469 | |
| 470 | w[nz] = -1/(dist*dist); |
| 471 | |
| 472 | diag_w += w[nz]; |
| 473 | |
| 474 | jd[nz] = ja[l]; |
| 475 | /* |
| 476 | d[nz] = w[nz]*(distance(x,dim,i,k)+distance(x,dim,k,ja[l])); |
| 477 | d[nz] = w[nz]*(dd[j]+dd[l]); |
| 478 | d[nz] = w[nz]*(avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]])*0.5; |
| 479 | */ |
| 480 | d[nz] = w[nz]*dist; |
| 481 | stop += d[nz]*distance(x,dim,ja[l],k); |
| 482 | sbot += d[nz]*dist; |
| 483 | diag_d += d[nz]; |
| 484 | |
| 485 | nz++; |
| 486 | } |
| 487 | } |
| 488 | } |
| 489 | jw[nz] = i; |
| 490 | lambda[i] *= (-diag_w);/* alternatively don't do that then we have a constant penalty term scaled by lambda0 */ |
| 491 | |
| 492 | w[nz] = -diag_w + lambda[i]; |
| 493 | jd[nz] = i; |
| 494 | d[nz] = -diag_d; |
| 495 | nz++; |
| 496 | |
| 497 | iw[i+1] = nz; |
| 498 | id[i+1] = nz; |
| 499 | } |
| 500 | s = stop/sbot; |
| 501 | for (i = 0; i < nz; i++) d[i] *= s; |
| 502 | |
| 503 | sm->scaling = s; |
| 504 | sm->Lw->nz = nz; |
| 505 | sm->Lwd->nz = nz; |
| 506 | |
| 507 | FREE(mask); |
| 508 | FREE(avg_dist); |
| 509 | SparseMatrix_delete(ID); |
| 510 | return sm; |
| 511 | } |
| 512 | |
| 513 | StressMajorizationSmoother SparseStressMajorizationSmoother_new(SparseMatrix A, int dim, real lambda0, real *x, |
| 514 | int weighting_scheme, int scale_initial_coord){ |
| 515 | /* solve a stress model to achieve the ideal distance among a sparse set of edges recorded in A. |
| 516 | A must be a real matrix. |
| 517 | */ |
| 518 | StressMajorizationSmoother sm; |
| 519 | int i, j, k, m = A->m, *ia, *ja, *iw, *jw, *id, *jd; |
| 520 | int nz; |
| 521 | real *d, *w, *lambda; |
| 522 | real diag_d, diag_w, *a, dist, s = 0, stop = 0, sbot = 0; |
| 523 | real xdot = 0; |
| 524 | |
| 525 | assert(SparseMatrix_is_symmetric(A, FALSE) && A->type == MATRIX_TYPE_REAL); |
| 526 | |
| 527 | /* if x is all zero, make it random */ |
| 528 | for (i = 0; i < m*dim; i++) xdot += x[i]*x[i]; |
| 529 | if (xdot == 0){ |
| 530 | for (i = 0; i < m*dim; i++) x[i] = 72*drand(); |
| 531 | } |
| 532 | |
| 533 | ia = A->ia; |
| 534 | ja = A->ja; |
| 535 | a = (real*) A->a; |
| 536 | |
| 537 | |
| 538 | sm = MALLOC(sizeof(struct StressMajorizationSmoother_struct)); |
| 539 | sm->scaling = 1.; |
| 540 | sm->data = NULL; |
| 541 | sm->scheme = SM_SCHEME_NORMAL; |
| 542 | sm->D = A; |
| 543 | sm->tol_cg = 0.01; |
| 544 | sm->maxit_cg = (int)sqrt((double) A->m); |
| 545 | |
| 546 | lambda = sm->lambda = MALLOC(sizeof(real)*m); |
| 547 | for (i = 0; i < m; i++) sm->lambda[i] = lambda0; |
| 548 | |
| 549 | nz = A->nz; |
| 550 | |
| 551 | sm->Lw = SparseMatrix_new(m, m, nz + m, MATRIX_TYPE_REAL, FORMAT_CSR); |
| 552 | sm->Lwd = SparseMatrix_new(m, m, nz + m, MATRIX_TYPE_REAL, FORMAT_CSR); |
| 553 | if (!(sm->Lw) || !(sm->Lwd)) { |
| 554 | StressMajorizationSmoother_delete(sm); |
| 555 | return NULL; |
| 556 | } |
| 557 | |
| 558 | iw = sm->Lw->ia; jw = sm->Lw->ja; |
| 559 | id = sm->Lwd->ia; jd = sm->Lwd->ja; |
| 560 | w = (real*) sm->Lw->a; d = (real*) sm->Lwd->a; |
| 561 | iw[0] = id[0] = 0; |
| 562 | |
| 563 | nz = 0; |
| 564 | for (i = 0; i < m; i++){ |
| 565 | diag_d = diag_w = 0; |
| 566 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 567 | k = ja[j]; |
| 568 | if (k != i){ |
| 569 | |
| 570 | jw[nz] = k; |
| 571 | dist = a[j]; |
| 572 | switch (weighting_scheme){ |
| 573 | case WEIGHTING_SCHEME_SQR_DIST: |
| 574 | if (dist*dist == 0){ |
| 575 | w[nz] = -100000; |
| 576 | } else { |
| 577 | w[nz] = -1/(dist*dist); |
| 578 | } |
| 579 | break; |
| 580 | case WEIGHTING_SCHEME_INV_DIST: |
| 581 | if (dist*dist == 0){ |
| 582 | w[nz] = -100000; |
| 583 | } else { |
| 584 | w[nz] = -1/(dist); |
| 585 | } |
| 586 | break; |
| 587 | case WEIGHTING_SCHEME_NONE: |
| 588 | w[nz] = -1; |
| 589 | break; |
| 590 | default: |
| 591 | assert(0); |
| 592 | return NULL; |
| 593 | } |
| 594 | diag_w += w[nz]; |
| 595 | jd[nz] = k; |
| 596 | d[nz] = w[nz]*dist; |
| 597 | |
| 598 | stop += d[nz]*distance(x,dim,i,k); |
| 599 | sbot += d[nz]*dist; |
| 600 | diag_d += d[nz]; |
| 601 | |
| 602 | nz++; |
| 603 | } |
| 604 | } |
| 605 | |
| 606 | jw[nz] = i; |
| 607 | lambda[i] *= (-diag_w);/* alternatively don't do that then we have a constant penalty term scaled by lambda0 */ |
| 608 | w[nz] = -diag_w + lambda[i]; |
| 609 | |
| 610 | jd[nz] = i; |
| 611 | d[nz] = -diag_d; |
| 612 | nz++; |
| 613 | |
| 614 | iw[i+1] = nz; |
| 615 | id[i+1] = nz; |
| 616 | } |
| 617 | if (scale_initial_coord){ |
| 618 | s = stop/sbot; |
| 619 | } else { |
| 620 | s = 1.; |
| 621 | } |
| 622 | if (s == 0) { |
| 623 | return NULL; |
| 624 | } |
| 625 | for (i = 0; i < nz; i++) d[i] *= s; |
| 626 | |
| 627 | |
| 628 | sm->scaling = s; |
| 629 | sm->Lw->nz = nz; |
| 630 | sm->Lwd->nz = nz; |
| 631 | |
| 632 | return sm; |
| 633 | } |
| 634 | |
| 635 | static real total_distance(int m, int dim, real* x, real* y){ |
| 636 | real total = 0, dist = 0; |
| 637 | int i, j; |
| 638 | |
| 639 | for (i = 0; i < m; i++){ |
| 640 | dist = 0.; |
| 641 | for (j = 0; j < dim; j++){ |
| 642 | dist += (y[i*dim+j] - x[i*dim+j])*(y[i*dim+j] - x[i*dim+j]); |
| 643 | } |
| 644 | total += sqrt(dist); |
| 645 | } |
| 646 | return total; |
| 647 | |
| 648 | } |
| 649 | |
| 650 | |
| 651 | |
| 652 | void SparseStressMajorizationSmoother_delete(SparseStressMajorizationSmoother sm){ |
| 653 | StressMajorizationSmoother_delete(sm); |
| 654 | } |
| 655 | |
| 656 | |
| 657 | real SparseStressMajorizationSmoother_smooth(SparseStressMajorizationSmoother sm, int dim, real *x, int maxit_sm, real tol){ |
| 658 | |
| 659 | return StressMajorizationSmoother_smooth(sm, dim, x, maxit_sm, tol); |
| 660 | |
| 661 | |
| 662 | } |
| 663 | static void get_edge_label_matrix(relative_position_constraints data, int m, int dim, real *x, SparseMatrix *LL, real **rhs){ |
| 664 | int edge_labeling_scheme = data->edge_labeling_scheme; |
| 665 | int n_constr_nodes = data->n_constr_nodes; |
| 666 | int *constr_nodes = data->constr_nodes; |
| 667 | SparseMatrix A_constr = data->A_constr; |
| 668 | int *ia = A_constr->ia, *ja = A_constr->ja, ii, jj, nz, l, ll, i, j; |
| 669 | int *irn = data->irn, *jcn = data->jcn; |
| 670 | real *val = data->val, dist, kk, k; |
| 671 | real *x00 = NULL; |
| 672 | SparseMatrix Lc = NULL; |
| 673 | real constr_penalty = data->constr_penalty; |
| 674 | |
| 675 | if (edge_labeling_scheme == ELSCHEME_PENALTY || edge_labeling_scheme == ELSCHEME_STRAIGHTLINE_PENALTY){ |
| 676 | /* for an node with two neighbors j--i--k, and assume i needs to be between j and k, then the contribution to P is |
| 677 | . i j k |
| 678 | i 1 -0.5 -0.5 |
| 679 | j -0.5 0.25 0.25 |
| 680 | k -0.5 0.25 0.25 |
| 681 | in general, if i has m neighbors j, k, ..., then |
| 682 | p_ii = 1 |
| 683 | p_jj = 1/m^2 |
| 684 | p_ij = -1/m |
| 685 | p jk = 1/m^2 |
| 686 | . i j k |
| 687 | i 1 -1/m -1/m ... |
| 688 | j -1/m 1/m^2 1/m^2 ... |
| 689 | k -1/m 1/m^2 1/m^2 ... |
| 690 | */ |
| 691 | if (!irn){ |
| 692 | assert((!jcn) && (!val)); |
| 693 | nz = 0; |
| 694 | for (i = 0; i < n_constr_nodes; i++){ |
| 695 | ii = constr_nodes[i]; |
| 696 | k = ia[ii+1] - ia[ii];/*usually k = 2 */ |
| 697 | nz += (int)((k+1)*(k+1)); |
| 698 | |
| 699 | } |
| 700 | irn = data->irn = MALLOC(sizeof(int)*nz); |
| 701 | jcn = data->jcn = MALLOC(sizeof(int)*nz); |
| 702 | val = data->val = MALLOC(sizeof(double)*nz); |
| 703 | } |
| 704 | nz = 0; |
| 705 | for (i = 0; i < n_constr_nodes; i++){ |
| 706 | ii = constr_nodes[i]; |
| 707 | jj = ja[ia[ii]]; ll = ja[ia[ii] + 1]; |
| 708 | if (jj == ll) continue; /* do not do loops */ |
| 709 | dist = distance_cropped(x, dim, jj, ll); |
| 710 | dist *= dist; |
| 711 | |
| 712 | k = ia[ii+1] - ia[ii];/* usually k = 2 */ |
| 713 | kk = k*k; |
| 714 | irn[nz] = ii; jcn[nz] = ii; val[nz++] = constr_penalty/(dist); |
| 715 | k = constr_penalty/(k*dist); kk = constr_penalty/(kk*dist); |
| 716 | for (j = ia[ii]; j < ia[ii+1]; j++){ |
| 717 | irn[nz] = ii; jcn[nz] = ja[j]; val[nz++] = -k; |
| 718 | } |
| 719 | for (j = ia[ii]; j < ia[ii+1]; j++){ |
| 720 | jj = ja[j]; |
| 721 | irn[nz] = jj; jcn[nz] = ii; val[nz++] = -k; |
| 722 | for (l = ia[ii]; l < ia[ii+1]; l++){ |
| 723 | ll = ja[l]; |
| 724 | irn[nz] = jj; jcn[nz] = ll; val[nz++] = kk; |
| 725 | } |
| 726 | } |
| 727 | } |
| 728 | Lc = SparseMatrix_from_coordinate_arrays(nz, m, m, irn, jcn, val, MATRIX_TYPE_REAL, sizeof(real)); |
| 729 | } else if (edge_labeling_scheme == ELSCHEME_PENALTY2 || edge_labeling_scheme == ELSCHEME_STRAIGHTLINE_PENALTY2){ |
| 730 | /* for an node with two neighbors j--i--k, and assume i needs to be between the old position of j and k, then the contribution to P is |
| 731 | 1/d_jk, and to the right hand side: {0,...,average_position_of_i's neighbor if i is an edge node,...} |
| 732 | */ |
| 733 | if (!irn){ |
| 734 | assert((!jcn) && (!val)); |
| 735 | nz = n_constr_nodes; |
| 736 | irn = data->irn = MALLOC(sizeof(int)*nz); |
| 737 | jcn = data->jcn = MALLOC(sizeof(int)*nz); |
| 738 | val = data->val = MALLOC(sizeof(double)*nz); |
| 739 | } |
| 740 | x00 = MALLOC(sizeof(real)*m*dim); |
| 741 | for (i = 0; i < m*dim; i++) x00[i] = 0; |
| 742 | nz = 0; |
| 743 | for (i = 0; i < n_constr_nodes; i++){ |
| 744 | ii = constr_nodes[i]; |
| 745 | jj = ja[ia[ii]]; ll = ja[ia[ii] + 1]; |
| 746 | dist = distance_cropped(x, dim, jj, ll); |
| 747 | irn[nz] = ii; jcn[nz] = ii; val[nz++] = constr_penalty/(dist); |
| 748 | for (j = ia[ii]; j < ia[ii+1]; j++){ |
| 749 | jj = ja[j]; |
| 750 | for (l = 0; l < dim; l++){ |
| 751 | x00[ii*dim+l] += x[jj*dim+l]; |
| 752 | } |
| 753 | } |
| 754 | for (l = 0; l < dim; l++) { |
| 755 | x00[ii*dim+l] *= constr_penalty/(dist)/(ia[ii+1] - ia[ii]); |
| 756 | } |
| 757 | } |
| 758 | Lc = SparseMatrix_from_coordinate_arrays(nz, m, m, irn, jcn, val, MATRIX_TYPE_REAL, sizeof(real)); |
| 759 | |
| 760 | } |
| 761 | *LL = Lc; |
| 762 | *rhs = x00; |
| 763 | } |
| 764 | |
| 765 | real get_stress(int m, int dim, int *iw, int *jw, real *w, real *d, real *x, real scaling, void *data, int weighted){ |
| 766 | int i, j; |
| 767 | real res = 0., dist; |
| 768 | /* we use the fact that d_ij = w_ij*graph_dist(i,j). Also, d_ij and x are scalinged by *scaling, so divide by it to get actual unscaled streee. */ |
| 769 | for (i = 0; i < m; i++){ |
| 770 | for (j = iw[i]; j < iw[i+1]; j++){ |
| 771 | if (i == jw[j]) { |
| 772 | continue; |
| 773 | } |
| 774 | dist = d[j]/w[j];/* both negative*/ |
| 775 | if (weighted){ |
| 776 | res += -w[j]*(dist - distance(x, dim, i, jw[j]))*(dist - distance(x, dim, i, jw[j])); |
| 777 | } else { |
| 778 | res += (dist - distance(x, dim, i, jw[j]))*(dist - distance(x, dim, i, jw[j])); |
| 779 | } |
| 780 | } |
| 781 | } |
| 782 | return 0.5*res/scaling/scaling; |
| 783 | |
| 784 | } |
| 785 | |
| 786 | static void uniform_stress_augment_rhs(int m, int dim, real *x, real *y, real alpha, real M){ |
| 787 | int i, j, k; |
| 788 | real dist, distij; |
| 789 | for (i = 0; i < m; i++){ |
| 790 | for (j = i+1; j < m; j++){ |
| 791 | dist = distance_cropped(x, dim, i, j); |
| 792 | for (k = 0; k < dim; k++){ |
| 793 | distij = (x[i*dim+k] - x[j*dim+k])/dist; |
| 794 | y[i*dim+k] += alpha*M*distij; |
| 795 | y[j*dim+k] += alpha*M*(-distij); |
| 796 | } |
| 797 | } |
| 798 | } |
| 799 | } |
| 800 | |
| 801 | static real uniform_stress_solve(SparseMatrix Lw, real alpha, int dim, real *x0, real *rhs, real tol, int maxit, int *flag){ |
| 802 | Operator Ax; |
| 803 | Operator Precon; |
| 804 | |
| 805 | Ax = Operator_uniform_stress_matmul(Lw, alpha); |
| 806 | Precon = Operator_uniform_stress_diag_precon_new(Lw, alpha); |
| 807 | |
| 808 | return cg(Ax, Precon, Lw->m, dim, x0, rhs, tol, maxit, flag); |
| 809 | |
| 810 | } |
| 811 | |
| 812 | real StressMajorizationSmoother_smooth(StressMajorizationSmoother sm, int dim, real *x, int maxit_sm, real tol) { |
| 813 | SparseMatrix Lw = sm->Lw, Lwd = sm->Lwd, Lwdd = NULL; |
| 814 | int i, j, k, m, *id, *jd, *iw, *jw, idiag, flag = 0, iter = 0; |
| 815 | real *w, *dd, *d, *y = NULL, *x0 = NULL, *x00 = NULL, diag, diff = 1, *lambda = sm->lambda, res, alpha = 0., M = 0.; |
| 816 | SparseMatrix Lc = NULL; |
| 817 | real dij, dist; |
| 818 | |
| 819 | |
| 820 | Lwdd = SparseMatrix_copy(Lwd); |
| 821 | m = Lw->m; |
| 822 | x0 = N_GNEW(dim*m,real); |
| 823 | if (!x0) goto RETURN; |
| 824 | |
| 825 | x0 = MEMCPY(x0, x, sizeof(real)*dim*m); |
| 826 | y = N_GNEW(dim*m,real); |
| 827 | if (!y) goto RETURN; |
| 828 | |
| 829 | id = Lwd->ia; jd = Lwd->ja; |
| 830 | d = (real*) Lwd->a; |
| 831 | dd = (real*) Lwdd->a; |
| 832 | w = (real*) Lw->a; |
| 833 | iw = Lw->ia; jw = Lw->ja; |
| 834 | |
| 835 | #ifdef DEBUG_PRINT |
| 836 | if (Verbose) fprintf(stderr, "initial stress = %f\n" , get_stress(m, dim, iw, jw, w, d, x, sm->scaling, sm->data, 1)); |
| 837 | #endif |
| 838 | /* for the additional matrix L due to the position constraints */ |
| 839 | if (sm->scheme == SM_SCHEME_NORMAL_ELABEL){ |
| 840 | get_edge_label_matrix(sm->data, m, dim, x, &Lc, &x00); |
| 841 | if (Lc) Lw = SparseMatrix_add(Lw, Lc); |
| 842 | } else if (sm->scheme == SM_SCHEME_UNIFORM_STRESS){ |
| 843 | alpha = ((real*) (sm->data))[0]; |
| 844 | M = ((real*) (sm->data))[1]; |
| 845 | } |
| 846 | |
| 847 | while (iter++ < maxit_sm && diff > tol){ |
| 848 | #ifdef GVIEWER |
| 849 | if (Gviewer) { |
| 850 | drawScene(); |
| 851 | if (iter%2 == 0) gviewer_dump_current_frame(); |
| 852 | } |
| 853 | #endif |
| 854 | |
| 855 | if (sm->scheme != SM_SCHEME_STRESS_APPROX){ |
| 856 | for (i = 0; i < m; i++){ |
| 857 | idiag = -1; |
| 858 | diag = 0.; |
| 859 | for (j = id[i]; j < id[i+1]; j++){ |
| 860 | if (i == jd[j]) { |
| 861 | idiag = j; |
| 862 | continue; |
| 863 | } |
| 864 | |
| 865 | dist = distance(x, dim, i, jd[j]); |
| 866 | //if (d[j] >= -0.0001*dist){ |
| 867 | // /* sometimes d[j] = 0 and ||x_i-x_j||=0*/ |
| 868 | // dd[j] = d[j]/MAX(0.0001, dist); |
| 869 | if (d[j] == 0){ |
| 870 | dd[j] = 0; |
| 871 | } else { |
| 872 | if (dist == 0){ |
| 873 | dij = d[j]/w[j];/* the ideal distance */ |
| 874 | /* perturb so points do not sit at the same place */ |
| 875 | for (k = 0; k < dim; k++) x[jd[j]*dim+k] += 0.0001*(drand()+.0001)*dij; |
| 876 | dist = distance(x, dim, i, jd[j]); |
| 877 | } |
| 878 | dd[j] = d[j]/dist; |
| 879 | |
| 880 | #if 0 |
| 881 | /* if two points are at the same place, we do not want a huge entry, |
| 882 | as this cause problem with CG./ In any case, |
| 883 | at thw limit d[j] == ||x[i] - x[jd[j]]||, |
| 884 | or close. */ |
| 885 | if (dist < -d[j]*0.0000001){ |
| 886 | dd[j] = -10000.; |
| 887 | } else { |
| 888 | dd[j] = d[j]/dist; |
| 889 | } |
| 890 | #endif |
| 891 | |
| 892 | } |
| 893 | diag += dd[j]; |
| 894 | } |
| 895 | assert(idiag >= 0); |
| 896 | dd[idiag] = -diag; |
| 897 | } |
| 898 | /* solve (Lw+lambda*I) x = Lwdd y + lambda x0 */ |
| 899 | |
| 900 | SparseMatrix_multiply_dense(Lwdd, FALSE, x, FALSE, &y, FALSE, dim); |
| 901 | } else { |
| 902 | for (i = 0; i < m; i++){ |
| 903 | for (j = 0; j < dim; j++){ |
| 904 | y[i*dim+j] = 0;/* for stress_approx scheme, the whole rhs is calculated in stress_maxent_augment_rhs */ |
| 905 | } |
| 906 | } |
| 907 | } |
| 908 | |
| 909 | if (lambda){/* is there a penalty term? */ |
| 910 | for (i = 0; i < m; i++){ |
| 911 | for (j = 0; j < dim; j++){ |
| 912 | y[i*dim+j] += lambda[i]*x0[i*dim+j]; |
| 913 | } |
| 914 | } |
| 915 | } |
| 916 | |
| 917 | /* additional term added to the rhs */ |
| 918 | switch (sm->scheme){ |
| 919 | case SM_SCHEME_NORMAL_ELABEL: { |
| 920 | for (i = 0; i < m; i++){ |
| 921 | for (j = 0; j < dim; j++){ |
| 922 | y[i*dim+j] += x00[i*dim+j]; |
| 923 | } |
| 924 | } |
| 925 | break; |
| 926 | } |
| 927 | case SM_SCHEME_UNIFORM_STRESS:{/* this part can be done more efficiently using octree approximation */ |
| 928 | uniform_stress_augment_rhs(m, dim, x, y, alpha, M); |
| 929 | break; |
| 930 | } |
| 931 | #if UNIMPEMENTED |
| 932 | case SM_SCHEME_MAXENT:{ |
| 933 | #ifdef GVIEWER |
| 934 | if (Gviewer){ |
| 935 | char *lab; |
| 936 | lab = MALLOC(sizeof(char)*100); |
| 937 | sprintf(lab,"maxent. alpha=%10.2g, iter=%d" ,stress_maxent_data_get_alpha(sm->data), iter); |
| 938 | gviewer_set_label(lab); |
| 939 | FREE(lab); |
| 940 | } |
| 941 | #endif |
| 942 | stress_maxent_augment_rhs_fast(sm, dim, x, y, &flag); |
| 943 | if (flag) goto RETURN; |
| 944 | break; |
| 945 | } |
| 946 | case SM_SCHEME_STRESS_APPROX:{ |
| 947 | stress_approx_augment_rhs(sm, dim, x, y, &flag); |
| 948 | if (flag) goto RETURN; |
| 949 | break; |
| 950 | } |
| 951 | case SM_SCHEME_STRESS:{ |
| 952 | #ifdef GVIEWER |
| 953 | if (Gviewer){ |
| 954 | char *lab; |
| 955 | lab = MALLOC(sizeof(char)*100); |
| 956 | sprintf(lab,"pmds(k), iter=%d" , iter); |
| 957 | gviewer_set_label(lab); |
| 958 | FREE(lab); |
| 959 | } |
| 960 | #endif |
| 961 | } |
| 962 | #endif /* UNIMPEMENTED */ |
| 963 | default: |
| 964 | break; |
| 965 | } |
| 966 | |
| 967 | #ifdef DEBUG_PRINT |
| 968 | if (Verbose) { |
| 969 | fprintf(stderr, "stress1 = %g\n" ,get_stress(m, dim, iw, jw, w, d, x, sm->scaling, sm->data, 1)); |
| 970 | } |
| 971 | #endif |
| 972 | |
| 973 | if (sm->scheme == SM_SCHEME_UNIFORM_STRESS){ |
| 974 | res = uniform_stress_solve(Lw, alpha, dim, x, y, sm->tol_cg, sm->maxit_cg, &flag); |
| 975 | } else { |
| 976 | res = SparseMatrix_solve(Lw, dim, x, y, sm->tol_cg, sm->maxit_cg, SOLVE_METHOD_CG, &flag); |
| 977 | //res = SparseMatrix_solve(Lw, dim, x, y, sm->tol_cg, 1, SOLVE_METHOD_JACOBI, &flag); |
| 978 | } |
| 979 | |
| 980 | if (flag) goto RETURN; |
| 981 | #ifdef DEBUG_PRINT |
| 982 | if (Verbose) fprintf(stderr, "stress2 = %g\n" ,get_stress(m, dim, iw, jw, w, d, y, sm->scaling, sm->data, 1)); |
| 983 | #endif |
| 984 | diff = total_distance(m, dim, x, y)/sqrt(vector_product(m*dim, x, x)); |
| 985 | #ifdef DEBUG_PRINT |
| 986 | if (Verbose){ |
| 987 | fprintf(stderr, "Outer iter = %d, cg res = %g, ||x_{k+1}-x_k||/||x_k|| = %g\n" ,iter, res, diff); |
| 988 | } |
| 989 | #endif |
| 990 | |
| 991 | |
| 992 | MEMCPY(x, y, sizeof(real)*m*dim); |
| 993 | } |
| 994 | |
| 995 | #ifdef DEBUG |
| 996 | _statistics[1] += iter-1; |
| 997 | #endif |
| 998 | |
| 999 | #ifdef DEBUG_PRINT |
| 1000 | if (Verbose) fprintf(stderr, "iter = %d, final stress = %f\n" , iter, get_stress(m, dim, iw, jw, w, d, x, sm->scaling, sm->data, 1)); |
| 1001 | #endif |
| 1002 | |
| 1003 | RETURN: |
| 1004 | SparseMatrix_delete(Lwdd); |
| 1005 | if (Lc) { |
| 1006 | SparseMatrix_delete(Lc); |
| 1007 | SparseMatrix_delete(Lw); |
| 1008 | } |
| 1009 | |
| 1010 | if (x0) FREE(x0); |
| 1011 | if (y) FREE(y); |
| 1012 | if (x00) FREE(x00); |
| 1013 | return diff; |
| 1014 | |
| 1015 | } |
| 1016 | |
| 1017 | void StressMajorizationSmoother_delete(StressMajorizationSmoother sm){ |
| 1018 | if (!sm) return; |
| 1019 | if (sm->Lw) SparseMatrix_delete(sm->Lw); |
| 1020 | if (sm->Lwd) SparseMatrix_delete(sm->Lwd); |
| 1021 | if (sm->lambda) FREE(sm->lambda); |
| 1022 | if (sm->data) sm->data_deallocator(sm->data); |
| 1023 | FREE(sm); |
| 1024 | } |
| 1025 | |
| 1026 | |
| 1027 | TriangleSmoother TriangleSmoother_new(SparseMatrix A, int dim, real lambda0, real *x, int use_triangularization){ |
| 1028 | TriangleSmoother sm; |
| 1029 | int i, j, k, m = A->m, *ia = A->ia, *ja = A->ja, *iw, *jw, jdiag, nz; |
| 1030 | SparseMatrix B; |
| 1031 | real *avg_dist, *lambda, *d, *w, diag_d, diag_w, dist; |
| 1032 | real s = 0, stop = 0, sbot = 0; |
| 1033 | |
| 1034 | assert(SparseMatrix_is_symmetric(A, FALSE)); |
| 1035 | |
| 1036 | avg_dist = N_GNEW(m,real); |
| 1037 | |
| 1038 | for (i = 0; i < m ;i++){ |
| 1039 | avg_dist[i] = 0; |
| 1040 | nz = 0; |
| 1041 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 1042 | if (i == ja[j]) continue; |
| 1043 | avg_dist[i] += distance(x, dim, i, ja[j]); |
| 1044 | nz++; |
| 1045 | } |
| 1046 | assert(nz > 0); |
| 1047 | avg_dist[i] /= nz; |
| 1048 | } |
| 1049 | |
| 1050 | sm = N_GNEW(1,struct TriangleSmoother_struct); |
| 1051 | sm->scaling = 1; |
| 1052 | sm->data = NULL; |
| 1053 | sm->scheme = SM_SCHEME_NORMAL; |
| 1054 | sm->tol_cg = 0.01; |
| 1055 | sm->maxit_cg = (int)sqrt((double) A->m); |
| 1056 | |
| 1057 | lambda = sm->lambda = N_GNEW(m,real); |
| 1058 | for (i = 0; i < m; i++) sm->lambda[i] = lambda0; |
| 1059 | |
| 1060 | if (m > 2){ |
| 1061 | if (use_triangularization){ |
| 1062 | B= call_tri(m, dim, x); |
| 1063 | } else { |
| 1064 | B= call_tri2(m, dim, x); |
| 1065 | } |
| 1066 | } else { |
| 1067 | B = SparseMatrix_copy(A); |
| 1068 | } |
| 1069 | |
| 1070 | |
| 1071 | |
| 1072 | sm->Lw = SparseMatrix_add(A, B); |
| 1073 | |
| 1074 | SparseMatrix_delete(B); |
| 1075 | sm->Lwd = SparseMatrix_copy(sm->Lw); |
| 1076 | if (!(sm->Lw) || !(sm->Lwd)) { |
| 1077 | TriangleSmoother_delete(sm); |
| 1078 | return NULL; |
| 1079 | } |
| 1080 | |
| 1081 | iw = sm->Lw->ia; jw = sm->Lw->ja; |
| 1082 | |
| 1083 | w = (real*) sm->Lw->a; d = (real*) sm->Lwd->a; |
| 1084 | |
| 1085 | for (i = 0; i < m; i++){ |
| 1086 | diag_d = diag_w = 0; |
| 1087 | jdiag = -1; |
| 1088 | for (j = iw[i]; j < iw[i+1]; j++){ |
| 1089 | k = jw[j]; |
| 1090 | if (k == i){ |
| 1091 | jdiag = j; |
| 1092 | continue; |
| 1093 | } |
| 1094 | /* w[j] = -1./(ia[i+1]-ia[i]+ia[ja[j]+1]-ia[ja[j]]); |
| 1095 | w[j] = -2./(avg_dist[i]+avg_dist[k]); |
| 1096 | w[j] = -1.*/;/* use unit weight for now, later can try 1/(deg(i)+deg(k)) */ |
| 1097 | dist = pow(distance_cropped(x,dim,i,k),0.6); |
| 1098 | w[j] = 1/(dist*dist); |
| 1099 | diag_w += w[j]; |
| 1100 | |
| 1101 | /* d[j] = w[j]*distance(x,dim,i,k); |
| 1102 | d[j] = w[j]*(avg_dist[i] + avg_dist[k])*0.5;*/ |
| 1103 | d[j] = w[j]*dist; |
| 1104 | stop += d[j]*distance(x,dim,i,k); |
| 1105 | sbot += d[j]*dist; |
| 1106 | diag_d += d[j]; |
| 1107 | |
| 1108 | } |
| 1109 | |
| 1110 | lambda[i] *= (-diag_w);/* alternatively don't do that then we have a constant penalty term scaled by lambda0 */ |
| 1111 | |
| 1112 | assert(jdiag >= 0); |
| 1113 | w[jdiag] = -diag_w + lambda[i]; |
| 1114 | d[jdiag] = -diag_d; |
| 1115 | } |
| 1116 | |
| 1117 | s = stop/sbot; |
| 1118 | for (i = 0; i < iw[m]; i++) d[i] *= s; |
| 1119 | sm->scaling = s; |
| 1120 | |
| 1121 | FREE(avg_dist); |
| 1122 | |
| 1123 | return sm; |
| 1124 | } |
| 1125 | |
| 1126 | void TriangleSmoother_delete(TriangleSmoother sm){ |
| 1127 | |
| 1128 | StressMajorizationSmoother_delete(sm); |
| 1129 | |
| 1130 | } |
| 1131 | |
| 1132 | void TriangleSmoother_smooth(TriangleSmoother sm, int dim, real *x){ |
| 1133 | |
| 1134 | StressMajorizationSmoother_smooth(sm, dim, x, 50, 0.001); |
| 1135 | } |
| 1136 | |
| 1137 | |
| 1138 | |
| 1139 | |
| 1140 | /* ================================ spring and spring-electrical based smoother ================ */ |
| 1141 | SpringSmoother SpringSmoother_new(SparseMatrix A, int dim, spring_electrical_control ctrl, real *x){ |
| 1142 | SpringSmoother sm; |
| 1143 | int i, j, k, l, m = A->m, *ia = A->ia, *ja = A->ja, *id, *jd; |
| 1144 | int *mask, nz; |
| 1145 | real *d, *dd; |
| 1146 | real *avg_dist; |
| 1147 | SparseMatrix ID = NULL; |
| 1148 | |
| 1149 | assert(SparseMatrix_is_symmetric(A, FALSE)); |
| 1150 | |
| 1151 | ID = ideal_distance_matrix(A, dim, x); |
| 1152 | dd = (real*) ID->a; |
| 1153 | |
| 1154 | sm = N_GNEW(1,struct SpringSmoother_struct); |
| 1155 | mask = N_GNEW(m,int); |
| 1156 | |
| 1157 | avg_dist = N_GNEW(m,real); |
| 1158 | |
| 1159 | for (i = 0; i < m ;i++){ |
| 1160 | avg_dist[i] = 0; |
| 1161 | nz = 0; |
| 1162 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 1163 | if (i == ja[j]) continue; |
| 1164 | avg_dist[i] += distance(x, dim, i, ja[j]); |
| 1165 | nz++; |
| 1166 | } |
| 1167 | assert(nz > 0); |
| 1168 | avg_dist[i] /= nz; |
| 1169 | } |
| 1170 | |
| 1171 | |
| 1172 | for (i = 0; i < m; i++) mask[i] = -1; |
| 1173 | |
| 1174 | nz = 0; |
| 1175 | for (i = 0; i < m; i++){ |
| 1176 | mask[i] = i; |
| 1177 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 1178 | k = ja[j]; |
| 1179 | if (mask[k] != i){ |
| 1180 | mask[k] = i; |
| 1181 | nz++; |
| 1182 | } |
| 1183 | } |
| 1184 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 1185 | k = ja[j]; |
| 1186 | for (l = ia[k]; l < ia[k+1]; l++){ |
| 1187 | if (mask[ja[l]] != i){ |
| 1188 | mask[ja[l]] = i; |
| 1189 | nz++; |
| 1190 | } |
| 1191 | } |
| 1192 | } |
| 1193 | } |
| 1194 | |
| 1195 | sm->D = SparseMatrix_new(m, m, nz, MATRIX_TYPE_REAL, FORMAT_CSR); |
| 1196 | if (!(sm->D)){ |
| 1197 | SpringSmoother_delete(sm); |
| 1198 | return NULL; |
| 1199 | } |
| 1200 | |
| 1201 | id = sm->D->ia; jd = sm->D->ja; |
| 1202 | d = (real*) sm->D->a; |
| 1203 | id[0] = 0; |
| 1204 | |
| 1205 | nz = 0; |
| 1206 | for (i = 0; i < m; i++){ |
| 1207 | mask[i] = i+m; |
| 1208 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 1209 | k = ja[j]; |
| 1210 | if (mask[k] != i+m){ |
| 1211 | mask[k] = i+m; |
| 1212 | jd[nz] = k; |
| 1213 | d[nz] = (avg_dist[i] + avg_dist[k])*0.5; |
| 1214 | d[nz] = dd[j]; |
| 1215 | nz++; |
| 1216 | } |
| 1217 | } |
| 1218 | |
| 1219 | for (j = ia[i]; j < ia[i+1]; j++){ |
| 1220 | k = ja[j]; |
| 1221 | for (l = ia[k]; l < ia[k+1]; l++){ |
| 1222 | if (mask[ja[l]] != i+m){ |
| 1223 | mask[ja[l]] = i+m; |
| 1224 | jd[nz] = ja[l]; |
| 1225 | d[nz] = (avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]])*0.5; |
| 1226 | d[nz] = dd[j]+dd[l]; |
| 1227 | nz++; |
| 1228 | } |
| 1229 | } |
| 1230 | } |
| 1231 | id[i+1] = nz; |
| 1232 | } |
| 1233 | sm->D->nz = nz; |
| 1234 | sm->ctrl = spring_electrical_control_new(); |
| 1235 | *(sm->ctrl) = *ctrl; |
| 1236 | sm->ctrl->random_start = FALSE; |
| 1237 | sm->ctrl->multilevels = 1; |
| 1238 | sm->ctrl->step /= 2; |
| 1239 | sm->ctrl->maxiter = 20; |
| 1240 | |
| 1241 | FREE(mask); |
| 1242 | FREE(avg_dist); |
| 1243 | SparseMatrix_delete(ID); |
| 1244 | |
| 1245 | return sm; |
| 1246 | } |
| 1247 | |
| 1248 | |
| 1249 | void SpringSmoother_delete(SpringSmoother sm){ |
| 1250 | if (!sm) return; |
| 1251 | if (sm->D) SparseMatrix_delete(sm->D); |
| 1252 | if (sm->ctrl) spring_electrical_control_delete(sm->ctrl); |
| 1253 | } |
| 1254 | |
| 1255 | |
| 1256 | |
| 1257 | |
| 1258 | void SpringSmoother_smooth(SpringSmoother sm, SparseMatrix A, real *node_weights, int dim, real *x){ |
| 1259 | int flag = 0; |
| 1260 | |
| 1261 | spring_electrical_spring_embedding(dim, A, sm->D, sm->ctrl, node_weights, x, &flag); |
| 1262 | assert(!flag); |
| 1263 | |
| 1264 | } |
| 1265 | |
| 1266 | /*=============================== end of spring and spring-electrical based smoother =========== */ |
| 1267 | |
| 1268 | void post_process_smoothing(int dim, SparseMatrix A, spring_electrical_control ctrl, real *node_weights, real *x, int *flag){ |
| 1269 | #ifdef TIME |
| 1270 | clock_t cpu; |
| 1271 | #endif |
| 1272 | |
| 1273 | #ifdef TIME |
| 1274 | cpu = clock(); |
| 1275 | #endif |
| 1276 | *flag = 0; |
| 1277 | |
| 1278 | switch (ctrl->smoothing){ |
| 1279 | case SMOOTHING_RNG: |
| 1280 | case SMOOTHING_TRIANGLE:{ |
| 1281 | TriangleSmoother sm; |
| 1282 | |
| 1283 | if (A->m > 2) { /* triangles need at least 3 nodes */ |
| 1284 | if (ctrl->smoothing == SMOOTHING_RNG){ |
| 1285 | sm = TriangleSmoother_new(A, dim, 0, x, FALSE); |
| 1286 | } else { |
| 1287 | sm = TriangleSmoother_new(A, dim, 0, x, TRUE); |
| 1288 | } |
| 1289 | TriangleSmoother_smooth(sm, dim, x); |
| 1290 | TriangleSmoother_delete(sm); |
| 1291 | } |
| 1292 | break; |
| 1293 | } |
| 1294 | case SMOOTHING_STRESS_MAJORIZATION_GRAPH_DIST: |
| 1295 | case SMOOTHING_STRESS_MAJORIZATION_POWER_DIST: |
| 1296 | case SMOOTHING_STRESS_MAJORIZATION_AVG_DIST: |
| 1297 | { |
| 1298 | StressMajorizationSmoother sm; |
| 1299 | int k, dist_scheme = IDEAL_AVG_DIST; |
| 1300 | |
| 1301 | if (ctrl->smoothing == SMOOTHING_STRESS_MAJORIZATION_GRAPH_DIST){ |
| 1302 | dist_scheme = IDEAL_GRAPH_DIST; |
| 1303 | } else if (ctrl->smoothing == SMOOTHING_STRESS_MAJORIZATION_AVG_DIST){ |
| 1304 | dist_scheme = IDEAL_AVG_DIST; |
| 1305 | } else if (ctrl->smoothing == SMOOTHING_STRESS_MAJORIZATION_POWER_DIST){ |
| 1306 | dist_scheme = IDEAL_POWER_DIST; |
| 1307 | } |
| 1308 | |
| 1309 | for (k = 0; k < 1; k++){ |
| 1310 | sm = StressMajorizationSmoother2_new(A, dim, 0.05, x, dist_scheme); |
| 1311 | StressMajorizationSmoother_smooth(sm, dim, x, 50, 0.001); |
| 1312 | StressMajorizationSmoother_delete(sm); |
| 1313 | } |
| 1314 | break; |
| 1315 | } |
| 1316 | case SMOOTHING_SPRING:{ |
| 1317 | SpringSmoother sm; |
| 1318 | int k; |
| 1319 | |
| 1320 | for (k = 0; k < 1; k++){ |
| 1321 | sm = SpringSmoother_new(A, dim, ctrl, x); |
| 1322 | SpringSmoother_smooth(sm, A, node_weights, dim, x); |
| 1323 | SpringSmoother_delete(sm); |
| 1324 | } |
| 1325 | |
| 1326 | break; |
| 1327 | } |
| 1328 | |
| 1329 | }/* end switch between smoothing methods */ |
| 1330 | |
| 1331 | #ifdef TIME |
| 1332 | if (Verbose) fprintf(stderr, "post processing %f\n" ,((real) (clock() - cpu)) / CLOCKS_PER_SEC); |
| 1333 | #endif |
| 1334 | } |
| 1335 | |