Graphviz 13.0.0~dev.20241220.2304
Loading...
Searching...
No Matches
post_process.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include <time.h>
14#include <string.h>
15#include <math.h>
16#include <stdbool.h>
17#include <stdlib.h>
18#include <common/types.h>
19#include <common/globals.h>
20
23#include <neatogen/overlap.h>
25#include <neatogen/call_tri.h>
26#include <sfdpgen/sfdp.h>
27#include <util/alloc.h>
28#include <util/exit.h>
29#include <util/unused.h>
30
31#define node_degree(i) (ia[(i)+1] - ia[(i)])
32
34 /* find the ideal distance between edges, either 1, or |N[i] \Union N[j]| - |N[i] \Intersection N[j]|
35 */
37 int *ia, *ja, i, j, k, l, nz;
38 double *d;
39 double len, di, sum, sumd;
40
41 assert(SparseMatrix_is_symmetric(A, false));
42
44 ia = D->ia;
45 ja = D->ja;
46 if (D->type != MATRIX_TYPE_REAL){
47 free(D->a);
48 D->type = MATRIX_TYPE_REAL;
49 D->a = gv_calloc(D->nz, sizeof(double));
50 }
51 d = D->a;
52
53 int *mask = gv_calloc(D->m, sizeof(int));
54 for (i = 0; i < D->m; i++) mask[i] = -1;
55
56 for (i = 0; i < D->m; i++){
57 di = node_degree(i);
58 mask[i] = i;
59 for (j = ia[i]; j < ia[i+1]; j++){
60 if (i == ja[j]) continue;
61 mask[ja[j]] = i;
62 }
63 for (j = ia[i]; j < ia[i+1]; j++){
64 k = ja[j];
65 if (i == k) continue;
66 len = di + node_degree(k);
67 for (l = ia[k]; l < ia[k+1]; l++){
68 if (mask[ja[l]] == i) len--;
69 }
70 d[j] = len;
71 assert(len > 0);
72 }
73
74 }
75
76 sum = 0; sumd = 0;
77 nz = 0;
78 for (i = 0; i < D->m; i++){
79 for (j = ia[i]; j < ia[i+1]; j++){
80 if (i == ja[j]) continue;
81 nz++;
82 sum += distance(x, dim, i, ja[j]);
83 sumd += d[j];
84 }
85 }
86 sum /= nz; sumd /= nz;
87 sum = sum/sumd;
88
89 for (i = 0; i < D->m; i++){
90 for (j = ia[i]; j < ia[i+1]; j++){
91 if (i == ja[j]) continue;
92 d[j] = sum*d[j];
93 }
94 }
95
96 free(mask);
97 return D;
98}
99
100
102 int ideal_dist_scheme){
103 /* use up to dist 2 neighbor. This is used in overcoming pherical effect with ideal distance of
104 2-neighbors equal graph distance etc.
105 */
106 int i, j, k, l, m = A->m, *ia = A->ia, *ja = A->ja, *iw, *jw, *id, *jd;
107 int nz;
108 double *d, *w, *lambda;
109 double diag_d, diag_w, dist, s = 0, stop = 0, sbot = 0;
111
112 assert(SparseMatrix_is_symmetric(A, false));
113
115
117 sm->scaling = 1.;
118 sm->data = NULL;
120 sm->tol_cg = 0.01;
121 sm->maxit_cg = floor(sqrt(A->m));
122
123 lambda = sm->lambda = gv_calloc(m, sizeof(double));
124 for (i = 0; i < m; i++) sm->lambda[i] = lambda0;
125 int *mask = gv_calloc(m, sizeof(int));
126
127 double *avg_dist = gv_calloc(m, sizeof(double));
128
129 for (i = 0; i < m ;i++){
130 avg_dist[i] = 0;
131 nz = 0;
132 for (j = ia[i]; j < ia[i+1]; j++){
133 if (i == ja[j]) continue;
134 avg_dist[i] += distance(x, dim, i, ja[j]);
135 nz++;
136 }
137 assert(nz > 0);
138 avg_dist[i] /= nz;
139 }
140
141
142 for (i = 0; i < m; i++) mask[i] = -1;
143
144 nz = 0;
145 for (i = 0; i < m; i++){
146 mask[i] = i;
147 for (j = ia[i]; j < ia[i+1]; j++){
148 k = ja[j];
149 if (mask[k] != i){
150 mask[k] = i;
151 nz++;
152 }
153 }
154 for (j = ia[i]; j < ia[i+1]; j++){
155 k = ja[j];
156 for (l = ia[k]; l < ia[k+1]; l++){
157 if (mask[ja[l]] != i){
158 mask[ja[l]] = i;
159 nz++;
160 }
161 }
162 }
163 }
164
165 sm->Lw = SparseMatrix_new(m, m, nz + m, MATRIX_TYPE_REAL, FORMAT_CSR);
166 sm->Lwd = SparseMatrix_new(m, m, nz + m, MATRIX_TYPE_REAL, FORMAT_CSR);
167 if (!(sm->Lw) || !(sm->Lwd)) {
169 return NULL;
170 }
171
172 iw = sm->Lw->ia; jw = sm->Lw->ja;
173
174 w = sm->Lw->a;
175 d = sm->Lwd->a;
176
177 id = sm->Lwd->ia; jd = sm->Lwd->ja;
178 iw[0] = id[0] = 0;
179
180 nz = 0;
181 for (i = 0; i < m; i++){
182 mask[i] = i+m;
183 diag_d = diag_w = 0;
184 for (j = ia[i]; j < ia[i+1]; j++){
185 k = ja[j];
186 if (mask[k] != i+m){
187 mask[k] = i+m;
188
189 jw[nz] = k;
190 if (ideal_dist_scheme == IDEAL_GRAPH_DIST){
191 dist = 1;
192 } else if (ideal_dist_scheme == IDEAL_AVG_DIST){
193 dist = (avg_dist[i] + avg_dist[k])*0.5;
194 } else if (ideal_dist_scheme == IDEAL_POWER_DIST){
195 dist = pow(distance_cropped(x,dim,i,k),.4);
196 } else {
197 fprintf(stderr,"ideal_dist_scheme value wrong");
198 assert(0);
199 graphviz_exit(1);
200 }
201
202 /*
203 w[nz] = -1./(ia[i+1]-ia[i]+ia[ja[j]+1]-ia[ja[j]]);
204 w[nz] = -2./(avg_dist[i]+avg_dist[k]);*/
205 /* w[nz] = -1.;*//* use unit weight for now, later can try 1/(deg(i)+deg(k)) */
206 w[nz] = -1/(dist*dist);
207
208 diag_w += w[nz];
209
210 jd[nz] = k;
211 /*
212 d[nz] = w[nz]*distance(x,dim,i,k);
213 d[nz] = w[nz]*dd[j];
214 d[nz] = w[nz]*(avg_dist[i] + avg_dist[k])*0.5;
215 */
216 d[nz] = w[nz]*dist;
217 stop += d[nz]*distance(x,dim,i,k);
218 sbot += d[nz]*dist;
219 diag_d += d[nz];
220
221 nz++;
222 }
223 }
224
225 /* distance 2 neighbors */
226 for (j = ia[i]; j < ia[i+1]; j++){
227 k = ja[j];
228 for (l = ia[k]; l < ia[k+1]; l++){
229 if (mask[ja[l]] != i+m){
230 mask[ja[l]] = i+m;
231
232 if (ideal_dist_scheme == IDEAL_GRAPH_DIST){
233 dist = 2;
234 } else if (ideal_dist_scheme == IDEAL_AVG_DIST){
235 dist = (avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]])*0.5;
236 } else if (ideal_dist_scheme == IDEAL_POWER_DIST){
237 dist = pow(distance_cropped(x,dim,i,ja[l]),.4);
238 } else {
239 fprintf(stderr,"ideal_dist_scheme value wrong");
240 assert(0);
241 graphviz_exit(1);
242 }
243
244 jw[nz] = ja[l];
245 /*
246 w[nz] = -1/(ia[i+1]-ia[i]+ia[ja[l]+1]-ia[ja[l]]);
247 w[nz] = -2/(avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]]);*/
248 /* w[nz] = -1.;*//* use unit weight for now, later can try 1/(deg(i)+deg(k)) */
249
250 w[nz] = -1/(dist*dist);
251
252 diag_w += w[nz];
253
254 jd[nz] = ja[l];
255 /*
256 d[nz] = w[nz]*(distance(x,dim,i,k)+distance(x,dim,k,ja[l]));
257 d[nz] = w[nz]*(dd[j]+dd[l]);
258 d[nz] = w[nz]*(avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]])*0.5;
259 */
260 d[nz] = w[nz]*dist;
261 stop += d[nz]*distance(x,dim,ja[l],k);
262 sbot += d[nz]*dist;
263 diag_d += d[nz];
264
265 nz++;
266 }
267 }
268 }
269 jw[nz] = i;
270 lambda[i] *= (-diag_w);/* alternatively don't do that then we have a constant penalty term scaled by lambda0 */
271
272 w[nz] = -diag_w + lambda[i];
273 jd[nz] = i;
274 d[nz] = -diag_d;
275 nz++;
276
277 iw[i+1] = nz;
278 id[i+1] = nz;
279 }
280 s = stop/sbot;
281 for (i = 0; i < nz; i++) d[i] *= s;
282
283 sm->scaling = s;
284 sm->Lw->nz = nz;
285 sm->Lwd->nz = nz;
286
287 free(mask);
288 free(avg_dist);
290 return sm;
291}
292
294 /* solve a stress model to achieve the ideal distance among a sparse set of edges recorded in A.
295 A must be a real matrix.
296 */
297 int i, j, k, m = A->m, *ia, *ja, *iw, *jw, *id, *jd;
298 int nz;
299 double *d, *w, *lambda;
300 double diag_d, diag_w, *a, dist, s = 0, stop = 0, sbot = 0;
301 double xdot = 0;
302
303 assert(SparseMatrix_is_symmetric(A, false) && A->type == MATRIX_TYPE_REAL);
304
305 /* if x is all zero, make it random */
306 for (i = 0; i < m*dim; i++) xdot += x[i]*x[i];
307 if (xdot == 0){
308 for (i = 0; i < m*dim; i++) x[i] = 72*drand();
309 }
310
311 ia = A->ia;
312 ja = A->ja;
313 a = A->a;
314
316 sm->scaling = 1.;
317 sm->data = NULL;
319 sm->D = A;
320 sm->tol_cg = 0.01;
321 sm->maxit_cg = floor(sqrt(A->m));
322
323 lambda = sm->lambda = gv_calloc(m, sizeof(double));
324
325 nz = A->nz;
326
327 sm->Lw = SparseMatrix_new(m, m, nz + m, MATRIX_TYPE_REAL, FORMAT_CSR);
328 sm->Lwd = SparseMatrix_new(m, m, nz + m, MATRIX_TYPE_REAL, FORMAT_CSR);
329 if (!(sm->Lw) || !(sm->Lwd)) {
331 return NULL;
332 }
333
334 iw = sm->Lw->ia; jw = sm->Lw->ja;
335 id = sm->Lwd->ia; jd = sm->Lwd->ja;
336 w = sm->Lw->a;
337 d = sm->Lwd->a;
338 iw[0] = id[0] = 0;
339
340 nz = 0;
341 for (i = 0; i < m; i++){
342 diag_d = diag_w = 0;
343 for (j = ia[i]; j < ia[i+1]; j++){
344 k = ja[j];
345 if (k != i){
346
347 jw[nz] = k;
348 dist = a[j];
349 w[nz] = -1;
350 diag_w += w[nz];
351 jd[nz] = k;
352 d[nz] = w[nz]*dist;
353
354 stop += d[nz]*distance(x,dim,i,k);
355 sbot += d[nz]*dist;
356 diag_d += d[nz];
357
358 nz++;
359 }
360 }
361
362 jw[nz] = i;
363 lambda[i] *= (-diag_w);/* alternatively don't do that then we have a constant penalty term scaled by lambda0 */
364 w[nz] = -diag_w + lambda[i];
365
366 jd[nz] = i;
367 d[nz] = -diag_d;
368 nz++;
369
370 iw[i+1] = nz;
371 id[i+1] = nz;
372 }
373 s = stop/sbot;
374 if (s == 0) {
376 return NULL;
377 }
378 for (i = 0; i < nz; i++) d[i] *= s;
379
380
381 sm->scaling = s;
382 sm->Lw->nz = nz;
383 sm->Lwd->nz = nz;
384
385 return sm;
386}
387
388static double total_distance(int m, int dim, double* x, double* y){
389 double total = 0, dist = 0;
390 int i, j;
391
392 for (i = 0; i < m; i++){
393 dist = 0.;
394 for (j = 0; j < dim; j++){
395 dist += (y[i*dim+j] - x[i*dim+j])*(y[i*dim+j] - x[i*dim+j]);
396 }
397 total += sqrt(dist);
398 }
399 return total;
400
401}
402
403
404
408
409
411 return StressMajorizationSmoother_smooth(sm, dim, x, maxit_sm);
412}
413
414static void get_edge_label_matrix(relative_position_constraints data, int m, int dim, double *x, SparseMatrix *LL, double **rhs){
415 int edge_labeling_scheme = data->edge_labeling_scheme;
416 int n_constr_nodes = data->n_constr_nodes;
417 int *constr_nodes = data->constr_nodes;
418 SparseMatrix A_constr = data->A_constr;
419 int *ia = A_constr->ia, *ja = A_constr->ja, ii, jj, nz, l, ll, i, j;
420 int *irn = data->irn, *jcn = data->jcn;
421 double *val = data->val, dist, kk, k;
422 double *x00 = NULL;
423 SparseMatrix Lc = NULL;
424 double constr_penalty = data->constr_penalty;
425
426 if (edge_labeling_scheme == ELSCHEME_PENALTY || edge_labeling_scheme == ELSCHEME_STRAIGHTLINE_PENALTY){
427 /* 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
428 . i j k
429 i 1 -0.5 -0.5
430 j -0.5 0.25 0.25
431 k -0.5 0.25 0.25
432 in general, if i has m neighbors j, k, ..., then
433 p_ii = 1
434 p_jj = 1/m^2
435 p_ij = -1/m
436 p jk = 1/m^2
437 . i j k
438 i 1 -1/m -1/m ...
439 j -1/m 1/m^2 1/m^2 ...
440 k -1/m 1/m^2 1/m^2 ...
441 */
442 if (!irn){
443 assert((!jcn) && (!val));
444 nz = 0;
445 for (i = 0; i < n_constr_nodes; i++){
446 ii = constr_nodes[i];
447 k = ia[ii+1] - ia[ii];/*usually k = 2 */
448 nz += (int)((k+1)*(k+1));
449
450 }
451 irn = data->irn = gv_calloc(nz, sizeof(int));
452 jcn = data->jcn = gv_calloc(nz, sizeof(int));
453 val = data->val = gv_calloc(nz, sizeof(double));
454 }
455 nz = 0;
456 for (i = 0; i < n_constr_nodes; i++){
457 ii = constr_nodes[i];
458 jj = ja[ia[ii]]; ll = ja[ia[ii] + 1];
459 if (jj == ll) continue; /* do not do loops */
460 dist = distance_cropped(x, dim, jj, ll);
461 dist *= dist;
462
463 k = ia[ii+1] - ia[ii];/* usually k = 2 */
464 kk = k*k;
465 irn[nz] = ii; jcn[nz] = ii; val[nz++] = constr_penalty/(dist);
466 k = constr_penalty/(k*dist); kk = constr_penalty/(kk*dist);
467 for (j = ia[ii]; j < ia[ii+1]; j++){
468 irn[nz] = ii; jcn[nz] = ja[j]; val[nz++] = -k;
469 }
470 for (j = ia[ii]; j < ia[ii+1]; j++){
471 jj = ja[j];
472 irn[nz] = jj; jcn[nz] = ii; val[nz++] = -k;
473 for (l = ia[ii]; l < ia[ii+1]; l++){
474 ll = ja[l];
475 irn[nz] = jj; jcn[nz] = ll; val[nz++] = kk;
476 }
477 }
478 }
479 Lc = SparseMatrix_from_coordinate_arrays(nz, m, m, irn, jcn, val, MATRIX_TYPE_REAL, sizeof(double));
480 } else if (edge_labeling_scheme == ELSCHEME_PENALTY2 || edge_labeling_scheme == ELSCHEME_STRAIGHTLINE_PENALTY2){
481 /* 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
482 1/d_jk, and to the right hand side: {0,...,average_position_of_i's neighbor if i is an edge node,...}
483 */
484 if (!irn){
485 assert((!jcn) && (!val));
486 nz = n_constr_nodes;
487 irn = data->irn = gv_calloc(nz, sizeof(int));
488 jcn = data->jcn = gv_calloc(nz, sizeof(int));
489 val = data->val = gv_calloc(nz, sizeof(double));
490 }
491 x00 = gv_calloc(m * dim, sizeof(double));
492 nz = 0;
493 for (i = 0; i < n_constr_nodes; i++){
494 ii = constr_nodes[i];
495 jj = ja[ia[ii]]; ll = ja[ia[ii] + 1];
496 dist = distance_cropped(x, dim, jj, ll);
497 irn[nz] = ii; jcn[nz] = ii; val[nz++] = constr_penalty/(dist);
498 for (j = ia[ii]; j < ia[ii+1]; j++){
499 jj = ja[j];
500 for (l = 0; l < dim; l++){
501 x00[ii*dim+l] += x[jj*dim+l];
502 }
503 }
504 for (l = 0; l < dim; l++) {
505 x00[ii*dim+l] *= constr_penalty/(dist)/(ia[ii+1] - ia[ii]);
506 }
507 }
508 Lc = SparseMatrix_from_coordinate_arrays(nz, m, m, irn, jcn, val, MATRIX_TYPE_REAL, sizeof(double));
509
510 }
511 *LL = Lc;
512 *rhs = x00;
513}
514
515static UNUSED double get_stress(int m, int dim, int *iw, int *jw, double *w,
516 double *d, double *x, double scaling) {
517 int i, j;
518 double res = 0., dist;
519 /* 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. */
520 for (i = 0; i < m; i++){
521 for (j = iw[i]; j < iw[i+1]; j++){
522 if (i == jw[j]) {
523 continue;
524 }
525 dist = d[j]/w[j];/* both negative*/
526 res += -w[j]*(dist - distance(x, dim, i, jw[j]))*(dist - distance(x, dim, i, jw[j]));
527 }
528 }
529 return 0.5*res/scaling/scaling;
530
531}
532
533double StressMajorizationSmoother_smooth(StressMajorizationSmoother sm, int dim, double *x, int maxit_sm) {
534 SparseMatrix Lw = sm->Lw, Lwd = sm->Lwd, Lwdd = NULL;
535 int i, j, k, m, *id, *jd, *iw, *jw, idiag, iter = 0;
536 double *w, *dd, *d, *y = NULL, *x0 = NULL, *x00 = NULL, diag, diff = 1, *lambda = sm->lambda;
537 SparseMatrix Lc = NULL;
538 double dij, dist;
539
540 const double tol = 0.001;
541
542
543 Lwdd = SparseMatrix_copy(Lwd);
544 m = Lw->m;
545 x0 = calloc(dim*m, sizeof(double));
546 if (!x0) goto RETURN;
547
548 memcpy(x0, x, sizeof(double)*dim*m);
549 y = calloc(dim*m, sizeof(double));
550 if (!y) goto RETURN;
551
552 id = Lwd->ia; jd = Lwd->ja;
553 d = Lwd->a;
554 dd = Lwdd->a;
555 w = Lw->a;
556 iw = Lw->ia; jw = Lw->ja;
557
558#ifdef DEBUG_PRINT
559 if (Verbose) fprintf(stderr, "initial stress = %f\n", get_stress(m, dim, iw, jw, w, d, x, sm->scaling));
560#else
561 (void)iw;
562 (void)jw;
563#endif
564 /* for the additional matrix L due to the position constraints */
566 get_edge_label_matrix(sm->data, m, dim, x, &Lc, &x00);
567 if (Lc) Lw = SparseMatrix_add(Lw, Lc);
568 }
569
570 while (iter++ < maxit_sm && diff > tol){
571
572 for (i = 0; i < m; i++){
573 idiag = -1;
574 diag = 0.;
575 for (j = id[i]; j < id[i+1]; j++){
576 if (i == jd[j]) {
577 idiag = j;
578 continue;
579 }
580
581 dist = distance(x, dim, i, jd[j]);
582 if (d[j] == 0){
583 dd[j] = 0;
584 } else {
585 if (dist == 0){
586 dij = d[j]/w[j];/* the ideal distance */
587 /* perturb so points do not sit at the same place */
588 for (k = 0; k < dim; k++) x[jd[j]*dim+k] += 0.0001*(drand()+.0001)*dij;
589 dist = distance(x, dim, i, jd[j]);
590 }
591 dd[j] = d[j]/dist;
592 }
593 diag += dd[j];
594 }
595 assert(idiag >= 0);
596 dd[idiag] = -diag;
597 }
598 /* solve (Lw+lambda*I) x = Lwdd y + lambda x0 */
599
600 SparseMatrix_multiply_dense(Lwdd, x, y, dim);
601
602 if (lambda){/* is there a penalty term? */
603 for (i = 0; i < m; i++){
604 for (j = 0; j < dim; j++){
605 y[i*dim+j] += lambda[i]*x0[i*dim+j];
606 }
607 }
608 }
609
610 /* additional term added to the rhs */
611 switch (sm->scheme){
613 for (i = 0; i < m; i++){
614 for (j = 0; j < dim; j++){
615 y[i*dim+j] += x00[i*dim+j];
616 }
617 }
618 break;
619 }
620 default:
621 break;
622 }
623
624#ifdef DEBUG_PRINT
625 if (Verbose) {
626 fprintf(stderr, "stress1 = %g\n",get_stress(m, dim, iw, jw, w, d, x, sm->scaling));
627 }
628#endif
629
630 SparseMatrix_solve(Lw, dim, x, y, sm->tol_cg, sm->maxit_cg);
631
632#ifdef DEBUG_PRINT
633 if (Verbose) fprintf(stderr, "stress2 = %g\n",get_stress(m, dim, iw, jw, w, d, y, sm->scaling));
634#endif
635 diff = total_distance(m, dim, x, y)/sqrt(vector_product(m*dim, x, x));
636#ifdef DEBUG_PRINT
637 if (Verbose){
638 fprintf(stderr, "Outer iter = %d, cg res = %g, ||x_{k+1}-x_k||/||x_k|| = %g\n",iter, res, diff);
639 }
640#endif
641
642
643 memcpy(x, y, sizeof(double)*m*dim);
644 }
645
646#ifdef DEBUG
647 _statistics[1] += iter-1;
648#endif
649
650#ifdef DEBUG_PRINT
651 if (Verbose) fprintf(stderr, "iter = %d, final stress = %f\n", iter, get_stress(m, dim, iw, jw, w, d, x, sm->scaling));
652#endif
653
654 RETURN:
656 if (Lc) {
659 }
660
661 free(x0);
662 free(y);
663 free(x00);
664 return diff;
665
666}
667
669 if (!sm) return;
670 if (sm->Lw) SparseMatrix_delete(sm->Lw);
671 if (sm->Lwd) SparseMatrix_delete(sm->Lwd);
672 free(sm->lambda);
673 if (sm->data) sm->data_deallocator(sm->data);
674 free(sm);
675}
676
678 bool use_triangularization) {
679 int i, j, k, m = A->m, *ia = A->ia, *ja = A->ja, *iw, *jw, jdiag, nz;
681 double *d, *w, diag_d, diag_w, dist;
682 double s = 0, stop = 0, sbot = 0;
683
684 assert(SparseMatrix_is_symmetric(A, false));
685
686 double *avg_dist = gv_calloc(m, sizeof(double));
687
688 for (i = 0; i < m ;i++){
689 avg_dist[i] = 0;
690 nz = 0;
691 for (j = ia[i]; j < ia[i+1]; j++){
692 if (i == ja[j]) continue;
693 avg_dist[i] += distance(x, dim, i, ja[j]);
694 nz++;
695 }
696 assert(nz > 0);
697 avg_dist[i] /= nz;
698 }
699
701 sm->scaling = 1;
702 sm->data = NULL;
704 sm->tol_cg = 0.01;
705 sm->maxit_cg = floor(sqrt(A->m));
706
707 double *lambda = sm->lambda = gv_calloc(m, sizeof(double));
708
709 if (m > 2){
710 if (use_triangularization){
711 B= call_tri(m, x);
712 } else {
713 B= call_tri2(m, dim, x);
714 }
715 } else {
717 }
718
719
720
721 sm->Lw = SparseMatrix_add(A, B);
722
724 sm->Lwd = SparseMatrix_copy(sm->Lw);
725 if (!(sm->Lw) || !(sm->Lwd)) {
727 return NULL;
728 }
729
730 iw = sm->Lw->ia; jw = sm->Lw->ja;
731
732 w = sm->Lw->a;
733 d = sm->Lwd->a;
734
735 for (i = 0; i < m; i++){
736 diag_d = diag_w = 0;
737 jdiag = -1;
738 for (j = iw[i]; j < iw[i+1]; j++){
739 k = jw[j];
740 if (k == i){
741 jdiag = j;
742 continue;
743 }
744 /* w[j] = -1./(ia[i+1]-ia[i]+ia[ja[j]+1]-ia[ja[j]]);
745 w[j] = -2./(avg_dist[i]+avg_dist[k]);
746 w[j] = -1.*/;/* use unit weight for now, later can try 1/(deg(i)+deg(k)) */
747 dist = pow(distance_cropped(x,dim,i,k),0.6);
748 w[j] = 1/(dist*dist);
749 diag_w += w[j];
750
751 /* d[j] = w[j]*distance(x,dim,i,k);
752 d[j] = w[j]*(avg_dist[i] + avg_dist[k])*0.5;*/
753 d[j] = w[j]*dist;
754 stop += d[j]*distance(x,dim,i,k);
755 sbot += d[j]*dist;
756 diag_d += d[j];
757
758 }
759
760 lambda[i] *= (-diag_w);
761
762 assert(jdiag >= 0);
763 w[jdiag] = -diag_w + lambda[i];
764 d[jdiag] = -diag_d;
765 }
766
767 s = stop/sbot;
768 for (i = 0; i < iw[m]; i++) d[i] *= s;
769 sm->scaling = s;
770
771 free(avg_dist);
772
773 return sm;
774}
775
781
785
786/* ================================ spring and spring-electrical based smoother ================ */
788 int i, j, k, l, m = A->m, *ia = A->ia, *ja = A->ja, *id, *jd;
789 int nz;
790 double *d, *dd;
792
793 assert(SparseMatrix_is_symmetric(A, false));
794
796 dd = ID->a;
797
798 SpringSmoother sm = gv_alloc(sizeof(struct SpringSmoother_struct));
799 int *mask = gv_calloc(m, sizeof(int));
800
801 double *avg_dist = gv_calloc(m, sizeof(double));
802
803 for (i = 0; i < m ;i++){
804 avg_dist[i] = 0;
805 nz = 0;
806 for (j = ia[i]; j < ia[i+1]; j++){
807 if (i == ja[j]) continue;
808 avg_dist[i] += distance(x, dim, i, ja[j]);
809 nz++;
810 }
811 assert(nz > 0);
812 avg_dist[i] /= nz;
813 }
814
815
816 for (i = 0; i < m; i++) mask[i] = -1;
817
818 nz = 0;
819 for (i = 0; i < m; i++){
820 mask[i] = i;
821 for (j = ia[i]; j < ia[i+1]; j++){
822 k = ja[j];
823 if (mask[k] != i){
824 mask[k] = i;
825 nz++;
826 }
827 }
828 for (j = ia[i]; j < ia[i+1]; j++){
829 k = ja[j];
830 for (l = ia[k]; l < ia[k+1]; l++){
831 if (mask[ja[l]] != i){
832 mask[ja[l]] = i;
833 nz++;
834 }
835 }
836 }
837 }
838
840 if (!(sm->D)){
842 return NULL;
843 }
844
845 id = sm->D->ia; jd = sm->D->ja;
846 d = sm->D->a;
847 id[0] = 0;
848
849 nz = 0;
850 for (i = 0; i < m; i++){
851 mask[i] = i+m;
852 for (j = ia[i]; j < ia[i+1]; j++){
853 k = ja[j];
854 if (mask[k] != i+m){
855 mask[k] = i+m;
856 jd[nz] = k;
857 d[nz] = (avg_dist[i] + avg_dist[k])*0.5;
858 d[nz] = dd[j];
859 nz++;
860 }
861 }
862
863 for (j = ia[i]; j < ia[i+1]; j++){
864 k = ja[j];
865 for (l = ia[k]; l < ia[k+1]; l++){
866 if (mask[ja[l]] != i+m){
867 mask[ja[l]] = i+m;
868 jd[nz] = ja[l];
869 d[nz] = (avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]])*0.5;
870 d[nz] = dd[j]+dd[l];
871 nz++;
872 }
873 }
874 }
875 id[i+1] = nz;
876 }
877 sm->D->nz = nz;
879 *(sm->ctrl) = *ctrl;
880 sm->ctrl->random_start = false;
881 sm->ctrl->multilevels = 1;
882 sm->ctrl->step /= 2;
883 sm->ctrl->maxiter = 20;
884
885 free(mask);
886 free(avg_dist);
888
889 return sm;
890}
891
892
894 if (!sm) return;
895 if (sm->D) SparseMatrix_delete(sm->D);
897}
898
899
900
901
903 int flag = 0;
904
905 spring_electrical_spring_embedding(dim, A, sm->D, sm->ctrl, x, &flag);
906 assert(!flag);
907
908}
909
910/*=============================== end of spring and spring-electrical based smoother =========== */
911
913#ifdef TIME
914 clock_t cpu;
915#endif
916
917#ifdef TIME
918 cpu = clock();
919#endif
920
921 switch (ctrl->smoothing){
922 case SMOOTHING_RNG:
923 case SMOOTHING_TRIANGLE:{
925
926 if (A->m > 2) { /* triangles need at least 3 nodes */
927 if (ctrl->smoothing == SMOOTHING_RNG){
928 sm = TriangleSmoother_new(A, dim, x, false);
929 } else {
930 sm = TriangleSmoother_new(A, dim, x, true);
931 }
934 }
935 break;
936 }
940 {
942 int dist_scheme = IDEAL_AVG_DIST;
943
945 dist_scheme = IDEAL_GRAPH_DIST;
947 dist_scheme = IDEAL_AVG_DIST;
949 dist_scheme = IDEAL_POWER_DIST;
950 }
951
952 sm = StressMajorizationSmoother2_new(A, dim, 0.05, x, dist_scheme);
955 break;
956 }
957 case SMOOTHING_SPRING:{
958 SpringSmoother sm = SpringSmoother_new(A, dim, ctrl, x);
959 SpringSmoother_smooth(sm, A, dim, x);
961
962 break;
963 }
964
965 }/* end switch between smoothing methods */
966
967#ifdef TIME
968 if (Verbose) fprintf(stderr, "post processing %f\n",((double) (clock() - cpu)) / CLOCKS_PER_SEC);
969#endif
970}
void SparseMatrix_multiply_dense(SparseMatrix A, const double *v, double *res, int dim)
bool SparseMatrix_is_symmetric(SparseMatrix A, bool test_pattern_symmetry_only)
SparseMatrix SparseMatrix_from_coordinate_arrays(int nz, int m, int n, int *irn, int *jcn, void *val0, int type, size_t sz)
void SparseMatrix_delete(SparseMatrix A)
SparseMatrix SparseMatrix_copy(SparseMatrix A)
SparseMatrix SparseMatrix_add(SparseMatrix A, SparseMatrix B)
SparseMatrix SparseMatrix_new(int m, int n, int nz, int type, int format)
@ MATRIX_TYPE_REAL
@ FORMAT_CSR
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
SparseMatrix call_tri2(int n, int dim, double *xx)
Definition call_tri.c:64
SparseMatrix call_tri(int n, double *x)
Definition call_tri.c:19
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
#define A(n, t)
Definition expr.h:76
static double dist(int dim, double *x, double *y)
double distance(double *x, int dim, int i, int j)
Definition general.c:145
double vector_product(int n, double *x, double *y)
Definition general.c:57
double distance_cropped(double *x, int dim, int i, int j)
Definition general.c:140
static double len(glCompPoint p)
Definition glutils.c:150
static bool Verbose
Definition gml2gv.c:23
#define ID
Definition gmlparse.c:376
void free(void *)
node NULL
Definition grammar.y:163
static double drand(void)
static uint64_t id
Definition gv2gml.c:42
#define B
Definition hierarchy.c:117
#define D
Definition hierarchy.c:119
static const int dim
@ ELSCHEME_PENALTY2
Definition overlap.h:29
@ ELSCHEME_STRAIGHTLINE_PENALTY2
Definition overlap.h:29
@ ELSCHEME_STRAIGHTLINE_PENALTY
Definition overlap.h:29
@ ELSCHEME_PENALTY
Definition overlap.h:29
static void get_edge_label_matrix(relative_position_constraints data, int m, int dim, double *x, SparseMatrix *LL, double **rhs)
StressMajorizationSmoother SparseStressMajorizationSmoother_new(SparseMatrix A, int dim, double *x)
static double total_distance(int m, int dim, double *x, double *y)
static SparseMatrix ideal_distance_matrix(SparseMatrix A, int dim, double *x)
void SparseStressMajorizationSmoother_delete(SparseStressMajorizationSmoother sm)
TriangleSmoother TriangleSmoother_new(SparseMatrix A, int dim, double *x, bool use_triangularization)
double StressMajorizationSmoother_smooth(StressMajorizationSmoother sm, int dim, double *x, int maxit_sm)
void SpringSmoother_delete(SpringSmoother sm)
double SparseStressMajorizationSmoother_smooth(SparseStressMajorizationSmoother sm, int dim, double *x, int maxit_sm)
void post_process_smoothing(int dim, SparseMatrix A, spring_electrical_control ctrl, double *x)
void TriangleSmoother_smooth(TriangleSmoother sm, int dim, double *x)
void StressMajorizationSmoother_delete(StressMajorizationSmoother sm)
SpringSmoother SpringSmoother_new(SparseMatrix A, int dim, spring_electrical_control ctrl, double *x)
StressMajorizationSmoother StressMajorizationSmoother2_new(SparseMatrix A, int dim, double lambda0, double *x, int ideal_dist_scheme)
static UNUSED double get_stress(int m, int dim, int *iw, int *jw, double *w, double *d, double *x, double scaling)
void TriangleSmoother_delete(TriangleSmoother sm)
void SpringSmoother_smooth(SpringSmoother sm, SparseMatrix A, int dim, double *x)
#define node_degree(i)
@ SM_SCHEME_NORMAL_ELABEL
@ SM_SCHEME_NORMAL
@ IDEAL_GRAPH_DIST
@ IDEAL_AVG_DIST
@ IDEAL_POWER_DIST
#define TriangleSmoother_struct
double SparseMatrix_solve(SparseMatrix A, int dim, double *x0, double *rhs, double tol, double maxit)
spring_electrical_control spring_electrical_control_new(void)
static const double tol
void spring_electrical_spring_embedding(int dim, SparseMatrix A0, SparseMatrix D, spring_electrical_control ctrl, double *x, int *flag)
void spring_electrical_control_delete(spring_electrical_control ctrl)
@ SMOOTHING_SPRING
@ SMOOTHING_RNG
@ SMOOTHING_STRESS_MAJORIZATION_GRAPH_DIST
@ SMOOTHING_STRESS_MAJORIZATION_POWER_DIST
@ SMOOTHING_TRIANGLE
@ SMOOTHING_STRESS_MAJORIZATION_AVG_DIST
#define RETURN(v)
Definition strmatch.c:144
spring_electrical_control ctrl
Definition legal.c:50
bool random_start
whether to apply SE from a random layout, or from exisiting layout
Definition xdot.h:155
graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t
Definition grammar.c:93
abstraction for squashing compiler warnings for unused symbols
#define UNUSED
Definition unused.h:25