Graphviz 14.1.2~dev.20251224.0902
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 <assert.h>
14#include <limits.h>
15#include <time.h>
16#include <string.h>
17#include <math.h>
18#include <stdbool.h>
19#include <stdlib.h>
20#include <common/types.h>
21#include <common/globals.h>
22
25#include <neatogen/overlap.h>
27#include <neatogen/call_tri.h>
28#include <sfdpgen/sfdp.h>
29#include <util/alloc.h>
30#include <util/exit.h>
31#include <util/gv_math.h>
32#include <util/unused.h>
33
34#define node_degree(i) (ia[(i)+1] - ia[(i)])
35
37 /* find the ideal distance between edges, either 1, or |N[i] \Union N[j]| - |N[i] \Intersection N[j]|
38 */
40 int *ia, *ja, i, j, k, l, nz;
41 double *d;
42 double len, di, sum, sumd;
43
44 assert(SparseMatrix_is_symmetric(A, false));
45
47 ia = D->ia;
48 ja = D->ja;
49 if (D->type != MATRIX_TYPE_REAL){
50 free(D->a);
51 D->type = MATRIX_TYPE_REAL;
52 D->a = gv_calloc(D->nz, sizeof(double));
53 }
54 d = D->a;
55
56 int *mask = gv_calloc(D->m, sizeof(int));
57 for (i = 0; i < D->m; i++) mask[i] = -1;
58
59 for (i = 0; i < D->m; i++){
60 di = node_degree(i);
61 mask[i] = i;
62 for (j = ia[i]; j < ia[i+1]; j++){
63 if (i == ja[j]) continue;
64 mask[ja[j]] = i;
65 }
66 for (j = ia[i]; j < ia[i+1]; j++){
67 k = ja[j];
68 if (i == k) continue;
69 len = di + node_degree(k);
70 for (l = ia[k]; l < ia[k+1]; l++){
71 if (mask[ja[l]] == i) len--;
72 }
73 d[j] = len;
74 assert(len > 0);
75 }
76
77 }
78
79 sum = 0; sumd = 0;
80 nz = 0;
81 for (i = 0; i < D->m; i++){
82 for (j = ia[i]; j < ia[i+1]; j++){
83 if (i == ja[j]) continue;
84 nz++;
85 sum += distance(x, dim, i, ja[j]);
86 sumd += d[j];
87 }
88 }
89 sum /= nz; sumd /= nz;
90 sum = sum/sumd;
91
92 for (i = 0; i < D->m; i++){
93 for (j = ia[i]; j < ia[i+1]; j++){
94 if (i == ja[j]) continue;
95 d[j] = sum*d[j];
96 }
97 }
98
99 free(mask);
100 return D;
101}
102
103
105 int ideal_dist_scheme){
106 /* use up to dist 2 neighbor. This is used in overcoming pherical effect with ideal distance of
107 2-neighbors equal graph distance etc.
108 */
109 int j, k, l, m = A->m, *ia = A->ia, *ja = A->ja, *iw, *jw, *id, *jd;
110 double *d, *w, *lambda;
111 double diag_d, diag_w, dist, s = 0, stop = 0, sbot = 0;
113
114 assert(SparseMatrix_is_symmetric(A, false));
115
117
119 sm->scaling = 1.;
120 sm->data = NULL;
122 sm->tol_cg = 0.01;
123 sm->maxit_cg = floor(sqrt(A->m));
124
125 lambda = sm->lambda = gv_calloc(m, sizeof(double));
126 for (int i = 0; i < m; i++) sm->lambda[i] = lambda0;
127 int *mask = gv_calloc(m, sizeof(int));
128
129 double *avg_dist = gv_calloc(m, sizeof(double));
130
131 for (int i = 0; i < m ;i++){
132 avg_dist[i] = 0;
133 int nz = 0;
134 for (j = ia[i]; j < ia[i+1]; j++){
135 if (i == ja[j]) continue;
136 avg_dist[i] += distance(x, dim, i, ja[j]);
137 nz++;
138 }
139 assert(nz > 0);
140 avg_dist[i] /= nz;
141 }
142
143 for (int i = 0; i < m; i++) mask[i] = -1;
144
145 size_t nz = 0;
146 for (int i = 0; i < m; i++){
147 mask[i] = i;
148 for (j = ia[i]; j < ia[i+1]; j++){
149 k = ja[j];
150 if (mask[k] != i){
151 mask[k] = i;
152 nz++;
153 }
154 }
155 for (j = ia[i]; j < ia[i+1]; j++){
156 k = ja[j];
157 for (l = ia[k]; l < ia[k+1]; l++){
158 if (mask[ja[l]] != i){
159 mask[ja[l]] = i;
160 nz++;
161 }
162 }
163 }
164 }
165
166 sm->Lw = SparseMatrix_new(m, m, nz + (size_t)m, MATRIX_TYPE_REAL, FORMAT_CSR);
167 assert(sm->Lw != NULL);
168 sm->Lwd = SparseMatrix_new(m, m, nz + (size_t)m, MATRIX_TYPE_REAL,
169 FORMAT_CSR);
170 assert(sm->Lwd != NULL);
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 (int 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 assert(nz <= INT_MAX);
278 iw[i + 1] = (int)nz;
279 id[i + 1] = (int)nz;
280 }
281 s = stop/sbot;
282 for (size_t i = 0; i < nz; i++) d[i] *= s;
283
284 sm->scaling = s;
285 sm->Lw->nz = nz;
286 sm->Lwd->nz = nz;
287
288 free(mask);
289 free(avg_dist);
291 return sm;
292}
293
295 /* solve a stress model to achieve the ideal distance among a sparse set of edges recorded in A.
296 A must be a real matrix.
297 */
298 int m = A->m;
299 double stop = 0, sbot = 0;
300
301 assert(SparseMatrix_is_symmetric(A, false) && A->type == MATRIX_TYPE_REAL);
302
303 /* if x is all zero, make it random */
304 bool has_nonzero = false;
305 for (int i = 0; i < m * dim; i++) {
306 if (!is_exactly_equal(x[i], 0)) {
307 has_nonzero = true;
308 break;
309 }
310 }
311 if (!has_nonzero) {
312 for (int i = 0; i < m*dim; i++) x[i] = 72*drand();
313 }
314
315 const int *const ia = A->ia;
316 const int *const ja = A->ja;
317 const double *const a = A->a;
318
320 sm->scaling = 1.;
321 sm->data = NULL;
323 sm->D = A;
324 sm->tol_cg = 0.01;
325 sm->maxit_cg = floor(sqrt(A->m));
326
327 double *const lambda = sm->lambda = gv_calloc(m, sizeof(double));
328
329 size_t nz = A->nz;
330
331 sm->Lw = SparseMatrix_new(m, m, nz + (size_t)m, MATRIX_TYPE_REAL, FORMAT_CSR);
332 assert(sm->Lw != NULL);
333 sm->Lwd = SparseMatrix_new(m, m, nz + (size_t)m, MATRIX_TYPE_REAL,
334 FORMAT_CSR);
335 assert(sm->Lwd != NULL);
336
337 int *const iw = sm->Lw->ia;
338 int *const jw = sm->Lw->ja;
339 int *const id = sm->Lwd->ia;
340 int *const jd = sm->Lwd->ja;
341 double *const w = sm->Lw->a;
342 double *const d = sm->Lwd->a;
343 iw[0] = id[0] = 0;
344
345 nz = 0;
346 for (int i = 0; i < m; i++){
347 double diag_d = 0;
348 double diag_w = 0;
349 for (int j = ia[i]; j < ia[i+1]; j++){
350 const int k = ja[j];
351 if (k != i){
352
353 jw[nz] = k;
354 const double dist = a[j];
355 w[nz] = -1;
356 diag_w += w[nz];
357 jd[nz] = k;
358 d[nz] = w[nz]*dist;
359
360 stop += d[nz]*distance(x,dim,i,k);
361 sbot += d[nz]*dist;
362 diag_d += d[nz];
363
364 nz++;
365 }
366 }
367
368 jw[nz] = i;
369 lambda[i] *= (-diag_w);/* alternatively don't do that then we have a constant penalty term scaled by lambda0 */
370 w[nz] = -diag_w + lambda[i];
371
372 jd[nz] = i;
373 d[nz] = -diag_d;
374 nz++;
375
376 assert(nz <= INT_MAX);
377 iw[i + 1] = (int)nz;
378 id[i + 1] = (int)nz;
379 }
380 const double s = stop / sbot;
381 if (s == 0) {
383 return NULL;
384 }
385 for (size_t i = 0; i < nz; i++) d[i] *= s;
386
387
388 sm->scaling = s;
389 sm->Lw->nz = nz;
390 sm->Lwd->nz = nz;
391
392 return sm;
393}
394
395static double total_distance(int m, int dim, double* x, double* y){
396 double total = 0, dist = 0;
397 int i, j;
398
399 for (i = 0; i < m; i++){
400 dist = 0.;
401 for (j = 0; j < dim; j++){
402 dist += (y[i*dim+j] - x[i*dim+j])*(y[i*dim+j] - x[i*dim+j]);
403 }
404 total += sqrt(dist);
405 }
406 return total;
407
408}
409
410
411
415
416
418 return StressMajorizationSmoother_smooth(sm, dim, x, maxit_sm);
419}
420
421static void get_edge_label_matrix(relative_position_constraints data, int m, int dim, double *x, SparseMatrix *LL, double **rhs){
422 int edge_labeling_scheme = data->edge_labeling_scheme;
423 int n_constr_nodes = data->n_constr_nodes;
424 int *constr_nodes = data->constr_nodes;
425 SparseMatrix A_constr = data->A_constr;
426 int *ia = A_constr->ia, *ja = A_constr->ja, ii, jj, l, ll, i, j;
427 int *irn = data->irn, *jcn = data->jcn;
428 double *val = data->val, dist, kk, k;
429 double *x00 = NULL;
430 SparseMatrix Lc = NULL;
431 double constr_penalty = data->constr_penalty;
432
433 if (edge_labeling_scheme == ELSCHEME_PENALTY || edge_labeling_scheme == ELSCHEME_STRAIGHTLINE_PENALTY){
434 /* 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
435 . i j k
436 i 1 -0.5 -0.5
437 j -0.5 0.25 0.25
438 k -0.5 0.25 0.25
439 in general, if i has m neighbors j, k, ..., then
440 pᵢᵢ = 1
441 pⱼⱼ = 1 ÷ m²
442 pᵢⱼ = -1 ÷ m
443 pⱼₖ = 1 ÷ m²
444 . i j k
445 i 1 -1 ÷ m -1 ÷ m ...
446 j -1 ÷ m 1 ÷ m² 1 ÷ m² ...
447 k -1 ÷ m 1 ÷ m² 1 ÷ m² ...
448 */
449 if (!irn){
450 assert((!jcn) && (!val));
451 size_t nz = 0;
452 for (i = 0; i < n_constr_nodes; i++){
453 ii = constr_nodes[i];
454 k = ia[ii+1] - ia[ii];/*usually k = 2 */
455 nz += (size_t)((k + 1) * (k + 1));
456
457 }
458 irn = data->irn = gv_calloc(nz, sizeof(int));
459 jcn = data->jcn = gv_calloc(nz, sizeof(int));
460 val = data->val = gv_calloc(nz, sizeof(double));
461 }
462 size_t nz = 0;
463 for (i = 0; i < n_constr_nodes; i++){
464 ii = constr_nodes[i];
465 jj = ja[ia[ii]]; ll = ja[ia[ii] + 1];
466 if (jj == ll) continue; /* do not do loops */
467 dist = distance_cropped(x, dim, jj, ll);
468 dist *= dist;
469
470 k = ia[ii+1] - ia[ii];/* usually k = 2 */
471 kk = k*k;
472 irn[nz] = ii; jcn[nz] = ii; val[nz++] = constr_penalty/(dist);
473 k = constr_penalty/(k*dist); kk = constr_penalty/(kk*dist);
474 for (j = ia[ii]; j < ia[ii+1]; j++){
475 irn[nz] = ii; jcn[nz] = ja[j]; val[nz++] = -k;
476 }
477 for (j = ia[ii]; j < ia[ii+1]; j++){
478 jj = ja[j];
479 irn[nz] = jj; jcn[nz] = ii; val[nz++] = -k;
480 for (l = ia[ii]; l < ia[ii+1]; l++){
481 ll = ja[l];
482 irn[nz] = jj; jcn[nz] = ll; val[nz++] = kk;
483 }
484 }
485 }
486 Lc = SparseMatrix_from_coordinate_arrays(nz, m, m, irn, jcn, val, MATRIX_TYPE_REAL, sizeof(double));
487 } else if (edge_labeling_scheme == ELSCHEME_PENALTY2 || edge_labeling_scheme == ELSCHEME_STRAIGHTLINE_PENALTY2){
488 /* 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
489 1/d_jk, and to the right hand side: {0,...,average_position_of_i's neighbor if i is an edge node,...}
490 */
491 if (!irn){
492 assert((!jcn) && (!val));
493 int nz = n_constr_nodes;
494 irn = data->irn = gv_calloc(nz, sizeof(int));
495 jcn = data->jcn = gv_calloc(nz, sizeof(int));
496 val = data->val = gv_calloc(nz, sizeof(double));
497 }
498 x00 = gv_calloc(m * dim, sizeof(double));
499 size_t nz = 0;
500 for (i = 0; i < n_constr_nodes; i++){
501 ii = constr_nodes[i];
502 jj = ja[ia[ii]]; ll = ja[ia[ii] + 1];
503 dist = distance_cropped(x, dim, jj, ll);
504 irn[nz] = ii; jcn[nz] = ii; val[nz++] = constr_penalty/(dist);
505 for (j = ia[ii]; j < ia[ii+1]; j++){
506 jj = ja[j];
507 for (l = 0; l < dim; l++){
508 x00[ii*dim+l] += x[jj*dim+l];
509 }
510 }
511 for (l = 0; l < dim; l++) {
512 x00[ii*dim+l] *= constr_penalty/(dist)/(ia[ii+1] - ia[ii]);
513 }
514 }
515 Lc = SparseMatrix_from_coordinate_arrays(nz, m, m, irn, jcn, val, MATRIX_TYPE_REAL, sizeof(double));
516
517 }
518 *LL = Lc;
519 *rhs = x00;
520}
521
522static UNUSED double get_stress(int m, int dim, int *iw, int *jw, double *w,
523 double *d, double *x, double scaling) {
524 int i, j;
525 double res = 0., dist;
526 // We use the fact that dᵢⱼ = wᵢⱼ × graph_dist(i, j). Also, dᵢⱼ and x are
527 // scaled by ×scaling, so divide by it to get actual unscaled stress.
528 for (i = 0; i < m; i++){
529 for (j = iw[i]; j < iw[i+1]; j++){
530 if (i == jw[j]) {
531 continue;
532 }
533 dist = d[j]/w[j];/* both negative*/
534 res += -w[j]*(dist - distance(x, dim, i, jw[j]))*(dist - distance(x, dim, i, jw[j]));
535 }
536 }
537 return 0.5*res/scaling/scaling;
538
539}
540
541double StressMajorizationSmoother_smooth(StressMajorizationSmoother sm, int dim, double *x, int maxit_sm) {
542 SparseMatrix Lw = sm->Lw, Lwd = sm->Lwd, Lwdd = NULL;
543 int i, j, k, m, *id, *jd, *iw, *jw, idiag, iter = 0;
544 double *w, *dd, *d, *y = NULL, *x0 = NULL, *x00 = NULL, diag, diff = 1, *lambda = sm->lambda;
545 SparseMatrix Lc = NULL;
546 double dij, dist;
547
548 const double tol = 0.001;
549
550
551 Lwdd = SparseMatrix_copy(Lwd);
552 m = Lw->m;
553 x0 = calloc(dim*m, sizeof(double));
554 if (!x0) goto RETURN;
555
556 memcpy(x0, x, sizeof(double)*dim*m);
557 y = calloc(dim*m, sizeof(double));
558 if (!y) goto RETURN;
559
560 id = Lwd->ia; jd = Lwd->ja;
561 d = Lwd->a;
562 dd = Lwdd->a;
563 w = Lw->a;
564 iw = Lw->ia; jw = Lw->ja;
565
566#ifdef DEBUG_PRINT
567 if (Verbose) fprintf(stderr, "initial stress = %f\n", get_stress(m, dim, iw, jw, w, d, x, sm->scaling));
568#else
569 (void)iw;
570 (void)jw;
571#endif
572 /* for the additional matrix L due to the position constraints */
574 get_edge_label_matrix(sm->data, m, dim, x, &Lc, &x00);
575 if (Lc) Lw = SparseMatrix_add(Lw, Lc);
576 }
577
578 while (iter++ < maxit_sm && diff > tol){
579
580 for (i = 0; i < m; i++){
581 idiag = -1;
582 diag = 0.;
583 for (j = id[i]; j < id[i+1]; j++){
584 if (i == jd[j]) {
585 idiag = j;
586 continue;
587 }
588
589 dist = distance(x, dim, i, jd[j]);
590 if (d[j] == 0){
591 dd[j] = 0;
592 } else {
593 if (dist == 0){
594 dij = d[j]/w[j];/* the ideal distance */
595 /* perturb so points do not sit at the same place */
596 for (k = 0; k < dim; k++) x[jd[j]*dim+k] += 0.0001*(drand()+.0001)*dij;
597 dist = distance(x, dim, i, jd[j]);
598 }
599 dd[j] = d[j]/dist;
600 }
601 diag += dd[j];
602 }
603 assert(idiag >= 0);
604 dd[idiag] = -diag;
605 }
606 /* solve (Lw+lambda*I) x = Lwdd y + lambda x0 */
607
608 SparseMatrix_multiply_dense(Lwdd, x, y, dim);
609
610 if (lambda){/* is there a penalty term? */
611 for (i = 0; i < m; i++){
612 for (j = 0; j < dim; j++){
613 y[i*dim+j] += lambda[i]*x0[i*dim+j];
614 }
615 }
616 }
617
618 /* additional term added to the rhs */
619 switch (sm->scheme){
621 for (i = 0; i < m; i++){
622 for (j = 0; j < dim; j++){
623 y[i*dim+j] += x00[i*dim+j];
624 }
625 }
626 break;
627 }
628 default:
629 break;
630 }
631
632#ifdef DEBUG_PRINT
633 if (Verbose) {
634 fprintf(stderr, "stress1 = %g\n",get_stress(m, dim, iw, jw, w, d, x, sm->scaling));
635 }
636#endif
637
638 SparseMatrix_solve(Lw, dim, x, y, sm->tol_cg, sm->maxit_cg);
639
640#ifdef DEBUG_PRINT
641 if (Verbose) fprintf(stderr, "stress2 = %g\n",get_stress(m, dim, iw, jw, w, d, y, sm->scaling));
642#endif
643 diff = total_distance(m, dim, x, y)/sqrt(vector_product(m*dim, x, x));
644#ifdef DEBUG_PRINT
645 if (Verbose){
646 fprintf(stderr, "Outer iter = %d, cg res = %g, ||x_{k+1}-x_k||/||x_k|| = %g\n",iter, res, diff);
647 }
648#endif
649
650
651 memcpy(x, y, sizeof(double)*m*dim);
652 }
653
654#ifdef DEBUG
655 _statistics[1] += iter-1;
656#endif
657
658#ifdef DEBUG_PRINT
659 if (Verbose) fprintf(stderr, "iter = %d, final stress = %f\n", iter, get_stress(m, dim, iw, jw, w, d, x, sm->scaling));
660#endif
661
662 RETURN:
664 if (Lc) {
667 }
668
669 free(x0);
670 free(y);
671 free(x00);
672 return diff;
673
674}
675
677 if (!sm) return;
678 if (sm->Lw) SparseMatrix_delete(sm->Lw);
679 if (sm->Lwd) SparseMatrix_delete(sm->Lwd);
680 free(sm->lambda);
681 if (sm->data) sm->data_deallocator(sm->data);
682 free(sm);
683}
684
686 bool use_triangularization) {
687 int i, j, k, m = A->m, *ia = A->ia, *ja = A->ja, *iw, *jw, jdiag, nz;
689 double *d, *w, diag_d, diag_w, dist;
690 double s = 0, stop = 0, sbot = 0;
691
692 assert(SparseMatrix_is_symmetric(A, false));
693
694 double *avg_dist = gv_calloc(m, sizeof(double));
695
696 for (i = 0; i < m ;i++){
697 avg_dist[i] = 0;
698 nz = 0;
699 for (j = ia[i]; j < ia[i+1]; j++){
700 if (i == ja[j]) continue;
701 avg_dist[i] += distance(x, dim, i, ja[j]);
702 nz++;
703 }
704 assert(nz > 0);
705 avg_dist[i] /= nz;
706 }
707
709 sm->scaling = 1;
710 sm->data = NULL;
712 sm->tol_cg = 0.01;
713 sm->maxit_cg = floor(sqrt(A->m));
714
715 double *lambda = sm->lambda = gv_calloc(m, sizeof(double));
716
717 if (m > 2){
718 if (use_triangularization){
719 B= call_tri(m, x);
720 } else {
721 B= call_tri2(m, dim, x);
722 }
723 } else {
725 }
726
727
728
729 sm->Lw = SparseMatrix_add(A, B);
730
732 sm->Lwd = SparseMatrix_copy(sm->Lw);
733 if (!sm->Lw || !sm->Lwd) {
735 return NULL;
736 }
737
738 iw = sm->Lw->ia; jw = sm->Lw->ja;
739
740 w = sm->Lw->a;
741 d = sm->Lwd->a;
742
743 for (i = 0; i < m; i++){
744 diag_d = diag_w = 0;
745 jdiag = -1;
746 for (j = iw[i]; j < iw[i+1]; j++){
747 k = jw[j];
748 if (k == i){
749 jdiag = j;
750 continue;
751 }
752 /* w[j] = -1./(ia[i+1]-ia[i]+ia[ja[j]+1]-ia[ja[j]]);
753 w[j] = -2./(avg_dist[i]+avg_dist[k]);
754 w[j] = -1.*/;/* use unit weight for now, later can try 1/(deg(i)+deg(k)) */
755 dist = pow(distance_cropped(x,dim,i,k),0.6);
756 w[j] = 1/(dist*dist);
757 diag_w += w[j];
758
759 /* d[j] = w[j]*distance(x,dim,i,k);
760 d[j] = w[j]*(avg_dist[i] + avg_dist[k])*0.5;*/
761 d[j] = w[j]*dist;
762 stop += d[j]*distance(x,dim,i,k);
763 sbot += d[j]*dist;
764 diag_d += d[j];
765
766 }
767
768 lambda[i] *= (-diag_w);
769
770 assert(jdiag >= 0);
771 w[jdiag] = -diag_w + lambda[i];
772 d[jdiag] = -diag_d;
773 }
774
775 s = stop/sbot;
776 for (i = 0; i < iw[m]; i++) d[i] *= s;
777 sm->scaling = s;
778
779 free(avg_dist);
780
781 return sm;
782}
783
789
793
794/* ================================ spring and spring-electrical based smoother ================ */
796 int j, k, l, m = A->m, *ia = A->ia, *ja = A->ja, *id, *jd;
797 double *d, *dd;
799
800 assert(SparseMatrix_is_symmetric(A, false));
801
803 dd = ID->a;
804
805 SpringSmoother sm = gv_alloc(sizeof(struct SpringSmoother_struct));
806 int *mask = gv_calloc(m, sizeof(int));
807
808 double *avg_dist = gv_calloc(m, sizeof(double));
809
810 for (int i = 0; i < m ;i++){
811 avg_dist[i] = 0;
812 int nz = 0;
813 for (j = ia[i]; j < ia[i+1]; j++){
814 if (i == ja[j]) continue;
815 avg_dist[i] += distance(x, dim, i, ja[j]);
816 nz++;
817 }
818 assert(nz > 0);
819 avg_dist[i] /= nz;
820 }
821
822 for (int i = 0; i < m; i++) mask[i] = -1;
823
824 size_t nz = 0;
825 for (int i = 0; i < m; i++){
826 mask[i] = i;
827 for (j = ia[i]; j < ia[i+1]; j++){
828 k = ja[j];
829 if (mask[k] != i){
830 mask[k] = i;
831 nz++;
832 }
833 }
834 for (j = ia[i]; j < ia[i+1]; j++){
835 k = ja[j];
836 for (l = ia[k]; l < ia[k+1]; l++){
837 if (mask[ja[l]] != i){
838 mask[ja[l]] = i;
839 nz++;
840 }
841 }
842 }
843 }
844
846 assert(sm->D != NULL);
847
848 id = sm->D->ia; jd = sm->D->ja;
849 d = sm->D->a;
850 id[0] = 0;
851
852 nz = 0;
853 for (int i = 0; i < m; i++){
854 mask[i] = i+m;
855 for (j = ia[i]; j < ia[i+1]; j++){
856 k = ja[j];
857 if (mask[k] != i+m){
858 mask[k] = i+m;
859 jd[nz] = k;
860 d[nz] = (avg_dist[i] + avg_dist[k])*0.5;
861 d[nz] = dd[j];
862 nz++;
863 }
864 }
865
866 for (j = ia[i]; j < ia[i+1]; j++){
867 k = ja[j];
868 for (l = ia[k]; l < ia[k+1]; l++){
869 if (mask[ja[l]] != i+m){
870 mask[ja[l]] = i+m;
871 jd[nz] = ja[l];
872 d[nz] = (avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]])*0.5;
873 d[nz] = dd[j]+dd[l];
874 nz++;
875 }
876 }
877 }
878 assert(nz <= INT_MAX);
879 id[i + 1] = (int)nz;
880 }
881 sm->D->nz = nz;
882 sm->ctrl = ctrl;
883 sm->ctrl.random_start = false;
884 sm->ctrl.multilevels = 1;
885 sm->ctrl.step /= 2;
886 sm->ctrl.maxiter = 20;
887
888 free(mask);
889 free(avg_dist);
891
892 return sm;
893}
894
895
897 if (!sm) return;
898 if (sm->D) SparseMatrix_delete(sm->D);
899}
900
902 int flag = 0;
903
904 spring_electrical_spring_embedding(dim, A, sm->D, &sm->ctrl, x, &flag);
905 assert(!flag);
906
907}
908
909/*=============================== end of spring and spring-electrical based smoother =========== */
910
912#ifdef TIME
913 clock_t cpu;
914#endif
915
916#ifdef TIME
917 cpu = clock();
918#endif
919
920 switch (ctrl.smoothing){
921 case SMOOTHING_RNG:
922 case SMOOTHING_TRIANGLE:{
924
925 if (A->m > 2) { /* triangles need at least 3 nodes */
926 if (ctrl.smoothing == SMOOTHING_RNG){
927 sm = TriangleSmoother_new(A, dim, x, false);
928 } else {
929 sm = TriangleSmoother_new(A, dim, x, true);
930 }
933 }
934 break;
935 }
939 {
941 int dist_scheme = IDEAL_AVG_DIST;
942
944 dist_scheme = IDEAL_GRAPH_DIST;
946 dist_scheme = IDEAL_AVG_DIST;
948 dist_scheme = IDEAL_POWER_DIST;
949 }
950
951 sm = StressMajorizationSmoother2_new(A, dim, 0.05, x, dist_scheme);
954 break;
955 }
956 case SMOOTHING_SPRING:{
957 SpringSmoother sm = SpringSmoother_new(A, dim, ctrl, x);
958 SpringSmoother_smooth(sm, A, dim, x);
960
961 break;
962 }
963
964 }/* end switch between smoothing methods */
965
966#ifdef TIME
967 if (Verbose) fprintf(stderr, "post processing %f\n",((double) (clock() - cpu)) / CLOCKS_PER_SEC);
968#endif
969}
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(size_t nz, int m, int n, int *irn, int *jcn, const void *val, 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, size_t 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 drand(void)
Definition general.c:22
double distance(double *x, int dim, int i, int j)
Definition general.c:121
double vector_product(int n, double *x, double *y)
Definition general.c:32
double distance_cropped(double *x, int dim, int i, int j)
Definition general.c:116
static double len(glCompPoint p)
Definition glutils.c:136
static bool Verbose
Definition gml2gv.c:24
void free(void *)
#define ID
Definition gmlparse.h:134
node NULL
Definition grammar.y:181
static uint64_t id
Definition gv2gml.c:40
Arithmetic helper functions.
static bool is_exactly_equal(double a, double b)
are two values precisely the same?
Definition gv_math.h:48
#define B
Definition hierarchy.c:118
#define D
Definition hierarchy.c:120
static const int dim
@ ELSCHEME_PENALTY2
Definition overlap.h:30
@ ELSCHEME_STRAIGHTLINE_PENALTY2
Definition overlap.h:30
@ ELSCHEME_STRAIGHTLINE_PENALTY
Definition overlap.h:30
@ ELSCHEME_PENALTY
Definition overlap.h:30
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)
@ IDEAL_GRAPH_DIST
@ IDEAL_AVG_DIST
@ IDEAL_POWER_DIST
#define TriangleSmoother_struct
@ SM_SCHEME_NORMAL_ELABEL
@ SM_SCHEME_NORMAL
double SparseMatrix_solve(SparseMatrix A, int dim, double *x0, double *rhs, double tol, double maxit)
static const double tol
void spring_electrical_spring_embedding(int dim, SparseMatrix A0, SparseMatrix D, spring_electrical_control *ctrl, double *x, int *flag)
@ 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
size_t nz
the actual length used is nz, for CSR/CSC matrix this is the same as ia[n]
spring_electrical_control ctrl
SparseMatrix Lwd
the laplacian like matrix with offdiag = -scaling × dᵢⱼ ÷ wᵢⱼ. RHS in stress majorization = Lwd....
SparseMatrix Lw
the weighted laplacian. with offdiag = -1 ÷ wᵢⱼ
bool random_start
whether to apply SE from a random layout, or from existing layout
graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t
Definition grammar.c:90
abstraction for squashing compiler warnings for unused symbols
#define UNUSED
Definition unused.h:25