Graphviz 15.1.1~dev.20260630.1303
Loading...
Searching...
No Matches
spring_electrical.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 v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12#include <assert.h>
13#include <sparse/SparseMatrix.h>
15#include <sparse/QuadTree.h>
16#include <sfdpgen/Multilevel.h>
18#include <neatogen/overlap.h>
19#include <common/types.h>
20#include <common/arith.h>
21#include <limits.h>
22#include <math.h>
23#include <common/globals.h>
24#include <stdbool.h>
25#include <stddef.h>
26#include <string.h>
27#include <time.h>
28#include <util/agxbuf.h>
29#include <util/alloc.h>
30#include <util/bitarray.h>
31#include <util/list.h>
32#include <util/prisize_t.h>
33
36static const double C = 0.2;
37
39static const int quadtree_size = 45;
40
43static const double bh = 0.6;
44
47static const double tol = 0.001;
48
49static const double cool = 0.90;
50
53 ctrl.p = AUTOP;
54 ctrl.random_start = true; // whether to apply SE from a random layout, or from existing layout
55 ctrl.K = -1;/* the natural distance. If K < 0, K will be set to the average distance of an edge */
56 ctrl.multilevels = 0;/* if <=1, single level */
57
58 ctrl.max_qtree_level = 10;/* max level of quadtree */
59 ctrl.maxiter = 500;
60 ctrl.step = 0.1;
61 ctrl.adaptive_cooling = true;
62 ctrl.random_seed = 123;
63 ctrl.beautify_leaves = false;
65 ctrl.overlap = 0;
66 ctrl.do_shrinking = true;
68 ctrl.initial_scaling = -4;
69 ctrl.rotation = 0.;
70 ctrl.edge_labeling_scheme = 0;
71 return ctrl;
72}
73
74static char* smoothings[] = {
75 "NONE", "STRESS_MAJORIZATION_GRAPH_DIST", "STRESS_MAJORIZATION_AVG_DIST", "STRESS_MAJORIZATION_POWER_DIST", "SPRING", "TRIANGLE", "RNG"
76};
77
78static char* tschemes[] = {
79 "NONE", "NORMAL", "FAST", "HYBRID"
80};
81
83 fprintf (stderr, "spring_electrical_control:\n");
84 fprintf (stderr, " repulsive exponent: %.03f\n", ctrl.p);
85 fprintf(stderr, " random start %d seed %d\n", (int)ctrl.random_start,
86 ctrl.random_seed);
87 fprintf (stderr, " K : %.03f C : %.03f\n", ctrl.K, C);
88 fprintf (stderr, " max levels %d\n", ctrl.multilevels);
89 fprintf (stderr, " quadtree size %d max_level %d\n", quadtree_size, ctrl.max_qtree_level);
90 fprintf (stderr, " Barnes-Hutt constant %.03f tolerance %.03f maxiter %d\n", bh, tol, ctrl.maxiter);
91 fprintf(stderr, " cooling %.03f step size %.03f adaptive %d\n", cool,
92 ctrl.step, (int)ctrl.adaptive_cooling);
93 fprintf (stderr, " beautify_leaves %d node weights %d rotation %.03f\n",
94 (int)ctrl.beautify_leaves, 0, ctrl.rotation);
95 fprintf (stderr, " smoothing %s overlap %d initial_scaling %.03f do_shrinking %d\n",
96 smoothings[ctrl.smoothing], ctrl.overlap, ctrl.initial_scaling, (int)ctrl.do_shrinking);
97 fprintf (stderr, " octree scheme %s\n", tschemes[ctrl.tscheme]);
98 fprintf (stderr, " edge_labeling_scheme %d\n", ctrl.edge_labeling_scheme);
99}
100
101enum { MAX_I = 20, OPT_UP = 1, OPT_DOWN = -1, OPT_INIT = 0 };
102
103typedef struct {
104 int i;
105 double work[MAX_I + 1];
108
110 oned_optimizer opt = {0};
111 opt.i = i;
112 opt.direction = OPT_INIT;
113 return opt;
114}
115
116static void oned_optimizer_train(oned_optimizer *opt, double work) {
117 int i = opt->i;
118
119 assert(i >= 0);
120 opt->work[i] = work;
121 if (opt->direction == OPT_INIT){
122 if (opt->i == MAX_I){
123 opt->direction = OPT_DOWN;
124 opt->i = opt->i - 1;
125 } else {
126 opt->direction = OPT_UP;
127 opt->i = MIN(MAX_I, opt->i + 1);
128 }
129 } else if (opt->direction == OPT_UP){
130 assert(i >= 1);
131 if (opt->work[i] < opt->work[i-1] && opt->i < MAX_I){
132 opt->i = MIN(MAX_I, opt->i + 1);
133 } else {
134 opt->i--;
135 opt->direction = OPT_DOWN;
136 }
137 } else {
138 assert(i < MAX_I);
139 if (opt->work[i] < opt->work[i+1] && opt->i > 0){
140 opt->i = MAX(0, opt->i-1);
141 } else {
142 opt->i++;
143 opt->direction = OPT_UP;
144 }
145 }
146}
147
148static int oned_optimizer_get(const oned_optimizer opt) {
149 return opt.i;
150}
151
152
154 double dist = 0, d;
155 int *ia = A->ia, *ja = A->ja, j, k;
156 assert(SparseMatrix_is_symmetric(A, true));
157
158 if (ia[A->m] == 0) return 1;
159 for (size_t i = 0; i < A->m; i++){
160 for (j = ia[i]; j < ia[i+1]; j++){
161 d = 0;
162 for (k = 0; k < dim; k++){
163 d += (coord[dim*(int)i+k] - coord[dim*ja[j]])*(coord[dim*(int)i+k] - coord[dim*ja[j]]);
164 }
165 dist += sqrt(d);
166 }
167 }
168 return dist/ia[A->m];
169}
170
171static double update_step(bool adaptive_cooling, double step, double Fnorm,
172 double Fnorm0) {
173
174 if (!adaptive_cooling) {
175 return cool*step;
176 }
177 if (Fnorm >= Fnorm0){
178 step = cool*step;
179 } else if (Fnorm > 0.95*Fnorm0){
180 // step = step;
181 } else {
182 step = 0.99*step/cool;
183 }
184 return step;
185}
186
187
188#define node_degree(i) (ia[(i)+1] - ia[(i)])
189
190static void set_leaves(double *x, int dim, double dist, double ang, int i, int j){
191 x[dim*j] = cos(ang)*dist + x[dim*i];
192 x[dim*j+1] = sin(ang)*dist + x[dim*i+1];
193}
194
195static void beautify_leaves(int dim, SparseMatrix A, double *x){
196 int j, *ia = A->ia, *ja = A->ja;
197 const size_t m = A->m;
198 int p;
199 double dist;
200 double step;
201
203
204 bitarray_t checked = bitarray_new(m);
205
206 for (size_t i = 0; i < m; i++){
207 if (ia[i+1] - ia[i] != 1) continue;
208 if (bitarray_get(checked, i)) continue;
209 p = ja[ia[i]];
210 if (!bitarray_get(checked, p)) {
211 bitarray_set(&checked, p, true);
212 dist = 0;
213 LIST(int) leaves = {0};
214 for (j = ia[p]; j < ia[p+1]; j++){
215 if (node_degree(ja[j]) == 1){
216 bitarray_set(&checked, ja[j], true);
217 dist += distance(x, dim, p, ja[j]);
218 LIST_APPEND(&leaves, ja[j]);
219 }
220 }
221 assert(!LIST_IS_EMPTY(&leaves));
222 dist /= (double)LIST_SIZE(&leaves);
223 double ang1 = 0;
224 double ang2 = 2 * M_PI;
225 const double pad = 0.1; // fudge factor to account for the size and
226 // placement of the nodes themselves
227 ang1 += pad;
228 ang2 -= pad;
229 assert(ang2 >= ang1);
230 step = 0.;
231 if (LIST_SIZE(&leaves) > 1) step = (ang2 - ang1) / (double)LIST_SIZE(&leaves);
232 for (size_t k = 0; k < LIST_SIZE(&leaves); k++) {
233 set_leaves(x, dim, dist, ang1, p, LIST_GET(&leaves, k));
234 ang1 += step;
235 }
236 LIST_FREE(&leaves);
237 }
238 }
239
240 bitarray_reset(&checked);
241}
242
245 double *x, int *flag) {
246 /* x is a point to a 1D array, x[i*dim+j] gives the coordinate of the i-th node at dimension j. */
247 SparseMatrix A = A0;
248 int n;
249 int i, j, k;
250 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
251 int *ia = NULL, *ja = NULL;
252 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
253 int iter = 0;
254 const bool adaptive_cooling = ctrl->adaptive_cooling;
255 double counts[4], *force = NULL;
256#ifdef TIME
257 clock_t start, end, start0;
258 double qtree_cpu = 0, qtree_new_cpu = 0;
259 double total_cpu = 0;
260 start0 = clock();
261#endif
262 int max_qtree_level = ctrl->max_qtree_level;
263
264 if (!A || maxiter <= 0) return;
265
266 const size_t m = A->m;
267 n = A->n;
268 if (n <= 0 || dim <= 0) return;
269
270 oned_optimizer qtree_level_optimizer = oned_optimizer_new(max_qtree_level);
271
272 *flag = 0;
273 if (m != (size_t)n) {
275 goto RETURN;
276 }
277 assert(A->format == FORMAT_CSR);
278 A = SparseMatrix_symmetrize(A, true);
279 ia = A->ia;
280 ja = A->ja;
281
282 if (ctrl->random_start){
283 srand(ctrl->random_seed);
284 for (i = 0; i < dim*n; i++) x[i] = drand();
285 }
286 if (K < 0){
287 ctrl->K = K = average_edge_length(A, dim, x);
288 }
289 if (p >= 0) ctrl->p = p = -1;
290 KP = pow(K, 1 - p);
291 CRK = pow(C, (2.-p)/3.)/K;
292
293 force = gv_calloc(dim * n, sizeof(double));
294
295 do {
296 iter++;
297 Fnorm0 = Fnorm;
298 Fnorm = 0.;
299
300 max_qtree_level = oned_optimizer_get(qtree_level_optimizer);
301
302#ifdef TIME
303 start = clock();
304#endif
305 QuadTree qt = QuadTree_new_from_point_list(dim, n, max_qtree_level, x);
306
307#ifdef TIME
308 qtree_new_cpu += (double)(clock() - start) / CLOCKS_PER_SEC;
309#endif
310
311 /* repulsive force */
312#ifdef TIME
313 start = clock();
314#endif
315
316 QuadTree_get_repulsive_force(qt, force, x, bh, p, KP, counts);
317
318#ifdef TIME
319 end = clock();
320 qtree_cpu += (double)(end - start) / CLOCKS_PER_SEC;
321#endif
322
323 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
324 for (i = 0; i < n; i++){
325 f = &(force[i*dim]);
326 for (j = ia[i]; j < ia[i+1]; j++){
327 if (ja[j] == i) continue;
328 dist = distance(x, dim, i, ja[j]);
329 for (k = 0; k < dim; k++){
330 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
331 }
332 }
333 }
334
335
336 /* move */
337 for (i = 0; i < n; i++){
338 f = &force[i*dim];
339 F = 0.;
340 for (k = 0; k < dim; k++) F += f[k]*f[k];
341 F = sqrt(F);
342 Fnorm += F;
343 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
344 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
345 }/* done vertex i */
346
347
348
349 if (qt) {
350#ifdef TIME
351 start = clock();
352#endif
353 QuadTree_delete(qt);
354#ifdef TIME
355 end = clock();
356 qtree_new_cpu += (double)(end - start) / CLOCKS_PER_SEC;
357#endif
358
359 oned_optimizer_train(&qtree_level_optimizer,
360 counts[0] + 0.85 * counts[1] + 3.3 * counts[2]);
361 } else {
362 if (Verbose) {
363 fprintf(stderr,
364 "\r iter = %d, step = %f Fnorm = %f nz = %"
365 PRISIZE_T " K = %f ", iter,
366 step, Fnorm, A->nz, K);
367 }
368 }
369
370 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
371 } while (step > tol && iter < maxiter);
372
373#ifdef DEBUG_PRINT
374 if (Verbose) {
375 fprintf(stderr, "\n iter = %d, step = %f Fnorm = %f nz = %" PRISIZE_T
376 " K = %f ", iter, step, Fnorm, A->nz, K);
377 }
378#endif
379
380 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
381
382#ifdef TIME
383 total_cpu += (double)(clock() - start0) / CLOCKS_PER_SEC;
384 if (Verbose) fprintf(stderr, "\n time for qtree = %f, qtree_force = %f, total cpu = %f\n",qtree_new_cpu, qtree_cpu, total_cpu);
385#endif
386
387
388 RETURN:
389 ctrl->max_qtree_level = max_qtree_level;
390
391 if (A != A0) SparseMatrix_delete(A);
392 free(force);
393}
394
397 double *x, int *flag) {
398 /* a version that does vertex moves in one go, instead of one at a time, use for debugging the fast version. Quadtree is not used. */
399 /* x is a point to a 1D array, x[i*dim+j] gives the coordinate of the i-th node at dimension j. */
400 SparseMatrix A = A0;
401 int n;
402 int i, j, k;
403 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
404 int *ia = NULL, *ja = NULL;
405 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
406 int iter = 0;
407 const bool adaptive_cooling = ctrl->adaptive_cooling;
408 double *force;
409#ifdef TIME
410 clock_t start, end, start0, start2;
411 double total_cpu = 0;
412 start0 = clock();
413#endif
414
415 fprintf(stderr,"spring_electrical_embedding_slow");
416 if (!A || maxiter <= 0) return;
417
418 const size_t m = A->m;
419 n = A->n;
420 if (n <= 0 || dim <= 0) return;
421 force = gv_calloc(n *dim, sizeof(double));
422
423 *flag = 0;
424 if (m != (size_t)n) {
426 goto RETURN;
427 }
428 assert(A->format == FORMAT_CSR);
429 A = SparseMatrix_symmetrize(A, true);
430 ia = A->ia;
431 ja = A->ja;
432
433 if (ctrl->random_start){
434 srand(ctrl->random_seed);
435 for (i = 0; i < dim*n; i++) x[i] = drand();
436 }
437 if (K < 0){
438 ctrl->K = K = average_edge_length(A, dim, x);
439 }
440 if (p >= 0) ctrl->p = p = -1;
441 KP = pow(K, 1 - p);
442 CRK = pow(C, (2.-p)/3.)/K;
443
444 f = gv_calloc(dim, sizeof(double));
445 do {
446 for (i = 0; i < dim*n; i++) force[i] = 0;
447
448 iter++;
449 Fnorm0 = Fnorm;
450 Fnorm = 0.;
451
452#ifdef TIME
453 start2 = clock();
454#endif
455
456
457 for (i = 0; i < n; i++){
458 for (k = 0; k < dim; k++) f[k] = 0.;
459 /* repulsive force K^(1 - p)/||x_i-x_j||^(1 - p) (x_i - x_j) */
460 for (j = 0; j < n; j++){
461 if (j == i) continue;
462 dist = distance_cropped(x, dim, i, j);
463 for (k = 0; k < dim; k++){
464 f[k] += KP*(x[i*dim+k] - x[j*dim+k])/pow(dist, 1.- p);
465 }
466 }
467 for (k = 0; k < dim; k++) force[i*dim+k] += f[k];
468 }
469
470
471
472 for (i = 0; i < n; i++){
473 for (k = 0; k < dim; k++) f[k] = 0.;
474 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
475 for (j = ia[i]; j < ia[i+1]; j++){
476 if (ja[j] == i) continue;
477 dist = distance(x, dim, i, ja[j]);
478 for (k = 0; k < dim; k++){
479 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
480 }
481 }
482 for (k = 0; k < dim; k++) force[i*dim+k] += f[k];
483 }
484
485
486
487 for (i = 0; i < n; i++){
488 /* normalize force */
489 for (k = 0; k < dim; k++) f[k] = force[i*dim+k];
490
491 F = 0.;
492 for (k = 0; k < dim; k++) F += f[k]*f[k];
493 F = sqrt(F);
494 Fnorm += F;
495
496 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
497
498 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
499
500 }/* done vertex i */
501
502 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
503 } while (step > tol && iter < maxiter);
504
505#ifdef DEBUG_PRINT
506 if (Verbose) {
507 fprintf(stderr, "iter = %d, step = %f Fnorm = %f nsuper = 0 nz = %d K = %f ",iter, step, Fnorm, A->nz,K);
508 }
509#endif
510
511 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
512
513#ifdef TIME
514 total_cpu += (double)(clock() - start0) / CLOCKS_PER_SEC;
515 if (Verbose) fprintf(stderr, "time for supernode = 0, total cpu = %f\n", total_cpu);
516#endif
517
518 RETURN:
519 if (A != A0) SparseMatrix_delete(A);
520 free(f);
521 free(force);
522}
523
526 double *x, int *flag) {
527 /* x is a point to a 1D array, x[i*dim+j] gives the coordinate of the i-th node at dimension j. */
528 SparseMatrix A = A0;
529 int n;
530 int i, j, k;
531 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
532 int *ia = NULL, *ja = NULL;
533 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
534 int iter = 0;
535 const bool adaptive_cooling = ctrl->adaptive_cooling;
536 bool USE_QT = false;
537 int nsuper = 0;
538 double *center = NULL, *supernode_wgts = NULL, *distances = NULL, nsuper_avg, counts = 0, counts_avg = 0;
539#ifdef TIME
540 clock_t start, end, start0, start2;
541 double qtree_cpu = 0;
542 double total_cpu = 0;
543 start0 = clock();
544#endif
545 int max_qtree_level = ctrl->max_qtree_level;
546 oned_optimizer qtree_level_optimizer = {0};
547
548 if (!A || maxiter <= 0) return;
549
550 const size_t m = A->m;
551 n = A->n;
552 if (n <= 0 || dim <= 0) return;
553
554 if (n >= quadtree_size) {
555 USE_QT = true;
556 qtree_level_optimizer = oned_optimizer_new(max_qtree_level);
557 }
558 *flag = 0;
559 if (m != (size_t)n) {
561 goto RETURN;
562 }
563 assert(A->format == FORMAT_CSR);
564 A = SparseMatrix_symmetrize(A, true);
565 ia = A->ia;
566 ja = A->ja;
567
568 if (ctrl->random_start){
569 srand(ctrl->random_seed);
570 for (i = 0; i < dim*n; i++) x[i] = drand();
571 }
572 if (K < 0){
573 ctrl->K = K = average_edge_length(A, dim, x);
574 }
575 if (p >= 0) ctrl->p = p = -1;
576 KP = pow(K, 1 - p);
577 CRK = pow(C, (2.-p)/3.)/K;
578
579 f = gv_calloc(dim, sizeof(double));
580 do {
581
582 iter++;
583 Fnorm0 = Fnorm;
584 Fnorm = 0.;
585 nsuper_avg = 0;
586 counts_avg = 0;
587
588 QuadTree qt = NULL;
589 if (USE_QT) {
590
591 max_qtree_level = oned_optimizer_get(qtree_level_optimizer);
592 qt = QuadTree_new_from_point_list(dim, n, max_qtree_level, x);
593
594
595 }
596#ifdef TIME
597 start2 = clock();
598#endif
599
600 for (i = 0; i < n; i++){
601 for (k = 0; k < dim; k++) f[k] = 0.;
602 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
603 for (j = ia[i]; j < ia[i+1]; j++){
604 if (ja[j] == i) continue;
605 dist = distance(x, dim, i, ja[j]);
606 for (k = 0; k < dim; k++){
607 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
608 }
609 }
610
611 /* repulsive force K^(1 - p)/||x_i-x_j||^(1 - p) (x_i - x_j) */
612 if (USE_QT){
613#ifdef TIME
614 start = clock();
615#endif
616 QuadTree_get_supernodes(qt, bh, &(x[dim*i]), i, &nsuper,
617 &center, &supernode_wgts, &distances, &counts);
618
619#ifdef TIME
620 end = clock();
621 qtree_cpu += (double)(end - start) / CLOCKS_PER_SEC;
622#endif
623 counts_avg += counts;
624 nsuper_avg += nsuper;
625 for (j = 0; j < nsuper; j++){
626 dist = MAX(distances[j], MINDIST);
627 for (k = 0; k < dim; k++){
628 f[k] += supernode_wgts[j]*KP*(x[i*dim+k] - center[j*dim+k])/pow(dist, 1.- p);
629 }
630 }
631 } else {
632 for (j = 0; j < n; j++){
633 if (j == i) continue;
634 dist = distance_cropped(x, dim, i, j);
635 for (k = 0; k < dim; k++){
636 f[k] += KP*(x[i*dim+k] - x[j*dim+k])/pow(dist, 1.- p);
637 }
638 }
639 }
640
641 /* normalize force */
642 F = 0.;
643 for (k = 0; k < dim; k++) F += f[k]*f[k];
644 F = sqrt(F);
645 Fnorm += F;
646
647 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
648
649 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
650
651 }/* done vertex i */
652
653 if (qt) {
654 QuadTree_delete(qt);
655 nsuper_avg /= n;
656 counts_avg /= n;
657 oned_optimizer_train(&qtree_level_optimizer, 5 * nsuper_avg + counts_avg);
658 }
659
660 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
661 } while (step > tol && iter < maxiter);
662
663#ifdef DEBUG_PRINT
664 if (Verbose) {
665 if (USE_QT){
666 fprintf(stderr, "iter = %d, step = %f Fnorm = %f qt_level = %d nsuper = %d nz = %d K = %f ",iter, step, Fnorm, max_qtree_level, (int) nsuper_avg,A->nz,K);
667 } else {
668 fprintf(stderr, "iter = %d, step = %f Fnorm = %f nsuper = %d nz = %d K = %f ",iter, step, Fnorm, (int) nsuper_avg,A->nz,K);
669 }
670 }
671#endif
672
673 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
674
675#ifdef TIME
676 total_cpu += (double)(clock() - start0) / CLOCKS_PER_SEC;
677 if (Verbose) fprintf(stderr, "time for supernode = %f, total cpu = %f\n",qtree_cpu, total_cpu);
678#endif
679
680 RETURN:
681 if (USE_QT) {
682 ctrl->max_qtree_level = max_qtree_level;
683 }
684 if (A != A0) SparseMatrix_delete(A);
685 free(f);
686 free(center);
687 free(supernode_wgts);
688 free(distances);
689}
690
693 double *x) {
694 /* x is a point to a 1D array, x[i*dim+j] gives the coordinate of the i-th node at dimension j. Same as the spring-electrical except we also
695 introduce force due to spring length
696 */
697 SparseMatrix A = A0;
698 int n;
699 int i, j, k;
700 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
701 int *ia = NULL, *ja = NULL;
702 int *id = NULL, *jd = NULL;
703 double *d;
704 double *xold = NULL;
705 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
706 int iter = 0;
707 const bool adaptive_cooling = ctrl->adaptive_cooling;
708 bool USE_QT = false;
709 int nsuper = 0;
710 double *center = NULL, *supernode_wgts = NULL, *distances = NULL, counts = 0;
711 int max_qtree_level = 10;
712
713 if (!A || maxiter <= 0) return;
714 n = A->n;
715 if (n <= 0 || dim <= 0) return;
716
717 if (n >= quadtree_size) {
718 USE_QT = true;
719 }
720 assert(A->m == (size_t)n);
721 assert(A->format == FORMAT_CSR);
722 A = SparseMatrix_symmetrize(A, true);
723 ia = A->ia;
724 ja = A->ja;
725 id = D->ia;
726 jd = D->ja;
727 d = D->a;
728
729 if (ctrl->random_start){
730 srand(ctrl->random_seed);
731 for (i = 0; i < dim*n; i++) x[i] = drand();
732 }
733 if (K < 0){
734 ctrl->K = K = average_edge_length(A, dim, x);
735 }
736 if (p >= 0) ctrl->p = p = -1;
737 KP = pow(K, 1 - p);
738 CRK = pow(C, (2.-p)/3.)/K;
739
740 f = gv_calloc(dim, sizeof(double));
741 xold = gv_calloc(dim * n, sizeof(double));
742 do {
743 iter++;
744 memcpy(xold, x, sizeof(double)*dim*n);
745 Fnorm0 = Fnorm;
746 Fnorm = 0.;
747
748 QuadTree qt = NULL;
749 if (USE_QT) {
750 qt = QuadTree_new_from_point_list(dim, n, max_qtree_level, x);
751 }
752
753 for (i = 0; i < n; i++){
754 for (k = 0; k < dim; k++) f[k] = 0.;
755 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
756
757 for (j = ia[i]; j < ia[i+1]; j++){
758 if (ja[j] == i) continue;
759 dist = distance(x, dim, i, ja[j]);
760 for (k = 0; k < dim; k++){
761 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
762 }
763 }
764
765 for (j = id[i]; j < id[i+1]; j++){
766 if (jd[j] == i) continue;
767 dist = distance_cropped(x, dim, i, jd[j]);
768 for (k = 0; k < dim; k++){
769 if (dist < d[j]){
770 f[k] += 0.2*CRK*(x[i*dim+k] - x[jd[j]*dim+k])*(dist - d[j])*(dist - d[j])/dist;
771 } else {
772 f[k] -= 0.2*CRK*(x[i*dim+k] - x[jd[j]*dim+k])*(dist - d[j])*(dist - d[j])/dist;
773 }
774 /* f[k] -= 0.2*CRK*(x[i*dim+k] - x[jd[j]*dim+k])*(dist - d[j]);*/
775 }
776 }
777
778 /* repulsive force K^(1 - p)/||x_i-x_j||^(1 - p) (x_i - x_j) */
779 if (USE_QT){
780 QuadTree_get_supernodes(qt, bh, &x[dim * i], i, &nsuper,
781 &center, &supernode_wgts, &distances, &counts);
782 for (j = 0; j < nsuper; j++){
783 dist = MAX(distances[j], MINDIST);
784 for (k = 0; k < dim; k++){
785 f[k] += supernode_wgts[j]*KP*(x[i*dim+k] - center[j*dim+k])/pow(dist, 1.- p);
786 }
787 }
788 } else {
789 for (j = 0; j < n; j++){
790 if (j == i) continue;
791 dist = distance_cropped(x, dim, i, j);
792 for (k = 0; k < dim; k++){
793 f[k] += KP*(x[i*dim+k] - x[j*dim+k])/pow(dist, 1.- p);
794 }
795 }
796 }
797
798 /* normalize force */
799 F = 0.;
800 for (k = 0; k < dim; k++) F += f[k]*f[k];
801 F = sqrt(F);
802 Fnorm += F;
803
804 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
805
806 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
807
808 }/* done vertex i */
809
810 if (qt) QuadTree_delete(qt);
811
812 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
813 } while (step > tol && iter < maxiter);
814
815 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
816
817 free(xold);
818 if (A != A0) SparseMatrix_delete(A);
819 free(f);
820 free(center);
821 free(supernode_wgts);
822 free(distances);
823}
824
825static void interpolate_coord(int dim, SparseMatrix A, double *x) {
826 int j, k, *ia = A->ia, *ja = A->ja, nz;
827 double alpha = 0.5, beta;
828
829 double *y = gv_calloc(dim, sizeof(double));
830 for (size_t i = 0; i < A->m; i++){
831 for (k = 0; k < dim; k++) y[k] = 0;
832 nz = 0;
833 for (j = ia[i]; j < ia[i+1]; j++){
834 if (ja[j] == (int)i) continue;
835 nz++;
836 for (k = 0; k < dim; k++){
837 y[k] += x[ja[j]*dim + k];
838 }
839 }
840 if (nz > 0){
841 beta = (1-alpha)/nz;
842 for (k = 0; k < dim; k++) x[(int)i*dim+k] = alpha*x[(int)i*dim+k] + beta*y[k];
843 }
844 }
845
846 free(y);
847}
848static void prolongate(int dim, SparseMatrix A, SparseMatrix P, SparseMatrix R, double *x, double *y, double delta){
849 int *ia, *ja, j, k;
851
853 const size_t nc = R->m;
854 ia = R->ia;
855 ja = R->ja;
856 for (size_t i = 0; i < nc; i++){
857 for (j = ia[i]+1; j < ia[i+1]; j++){
858 for (k = 0; k < dim; k++){
859 y[ja[j]*dim + k] += delta*(drand() - 0.5);
860 }
861 }
862 }
863}
864
866 int max = 0, *ia = A->ia, *ja = A->ja, j, deg;
867 bool res = false;
868 const size_t m = A->m;
869 int *mask = gv_calloc(m + 1, sizeof(int));
870
871 for (size_t i = 0; i < m + 1; i++){
872 mask[i] = 0;
873 }
874
875 for (size_t i = 0; i < m; i++){
876 deg = 0;
877 for (j = ia[i]; j < ia[i+1]; j++){
878 if ((int)i == ja[j]) continue;
879 deg++;
880 }
881 mask[deg]++;
882 max = MAX(max, mask[deg]);
883 }
884 if (mask[1] > 0.8*max && mask[1] > 0.3 * (double)m) res = true;
885 free(mask);
886 return res;
887}
888
889void pcp_rotate(int n, int dim, double *x){
890 int i, k,l;
891 double y[4], axis[2], center[2], dist, x0, x1;
892
893 assert(dim == 2);
894 for (i = 0; i < dim*dim; i++) y[i] = 0;
895 for (i = 0; i < dim; i++) center[i] = 0;
896 for (i = 0; i < n; i++){
897 for (k = 0; k < dim; k++){
898 center[k] += x[i*dim+k];
899 }
900 }
901 for (i = 0; i < dim; i++) center[i] /= n;
902 for (i = 0; i < n; i++){
903 for (k = 0; k < dim; k++){
904 x[dim*i+k] = x[dim*i+k] - center[k];
905 }
906 }
907
908 for (i = 0; i < n; i++){
909 for (k = 0; k < dim; k++){
910 for (l = 0; l < dim; l++){
911 y[dim*k+l] += x[i*dim+k]*x[i*dim+l];
912 }
913 }
914 }
915 if (y[1] == 0) {
916 axis[0] = 0; axis[1] = 1;
917 } else {
918 /* Eigensystem[{{x0, x1}, {x1, x3}}] =
919 {{(x0 + x3 - Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/2,
920 (x0 + x3 + Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/2},
921 {{-(-x0 + x3 + Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/(2*x1), 1},
922 {-(-x0 + x3 - Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/(2*x1), 1}}}
923 */
924 axis[0] = -(-y[0] + y[3] - sqrt(y[0]*y[0]+4*y[1]*y[1]-2*y[0]*y[3]+y[3]*y[3]))/(2*y[1]);
925 axis[1] = 1;
926 }
927 dist = sqrt(1+axis[0]*axis[0]);
928 axis[0] = axis[0]/dist;
929 axis[1] = axis[1]/dist;
930 for (i = 0; i < n; i++){
931 x0 = x[dim*i]*axis[0]+x[dim*i+1]*axis[1];
932 x1 = -x[dim*i]*axis[1]+x[dim*i+1]*axis[0];
933 x[dim*i] = x0;
934 x[dim*i + 1] = x1;
935
936 }
937
938
939}
940
941static void rotate(int n, int dim, double *x, double angle){
942 int i, k;
943 double axis[2], center[2], x0, x1;
944 double radian = 3.14159/180;
945
946 assert(dim == 2);
947 for (i = 0; i < dim; i++) center[i] = 0;
948 for (i = 0; i < n; i++){
949 for (k = 0; k < dim; k++){
950 center[k] += x[i*dim+k];
951 }
952 }
953 for (i = 0; i < dim; i++) center[i] /= n;
954 for (i = 0; i < n; i++){
955 for (k = 0; k < dim; k++){
956 x[dim*i+k] = x[dim*i+k] - center[k];
957 }
958 }
959 axis[0] = cos(-angle*radian);
960 axis[1] = sin(-angle*radian);
961 for (i = 0; i < n; i++){
962 x0 = x[dim*i]*axis[0]+x[dim*i+1]*axis[1];
963 x1 = -x[dim*i]*axis[1]+x[dim*i+1]*axis[0];
964 x[dim*i] = x0;
965 x[dim*i + 1] = x1;
966 }
967
968
969}
970
971static void attach_edge_label_coordinates(int dim, SparseMatrix A, int n_edge_label_nodes, int *edge_label_nodes, double *x, double *x2){
972 int ii, j, k;
973 int nnodes = 0;
974 double len;
975
976 int *mask = gv_calloc(A->m, sizeof(int));
977
978 for (size_t i = 0; i < A->m; i++) mask[i] = 1;
979 for (int i = 0; i < n_edge_label_nodes; i++) {
980 if (edge_label_nodes[i] >= 0 && (size_t)edge_label_nodes[i] < A->m) mask[edge_label_nodes[i]] = -1;
981 }
982
983 for (size_t i = 0; i < A->m; i++) {
984 if (mask[i] >= 0) mask[i] = nnodes++;
985 }
986
987
988 for (size_t i = 0; i < A->m; i++){
989 if (mask[i] >= 0){
990 for (k = 0; k < dim; k++) x[(int)i * dim + k] = x2[mask[i]*dim+k];
991 }
992 }
993
994 for (int i = 0; i < n_edge_label_nodes; i++){
995 ii = edge_label_nodes[i];
996 len = A->ia[ii+1] - A->ia[ii];
997 assert(len >= 2); /* should just be 2 */
998 assert(mask[ii] < 0);
999 for (k = 0; k < dim; k++) {
1000 x[ii*dim+k] = 0;
1001 }
1002 for (j = A->ia[ii]; j < A->ia[ii+1]; j++){
1003 for (k = 0; k < dim; k++){
1004 x[ii * dim + k] += x[A->ja[j] * dim + k];
1005 }
1006 }
1007 for (k = 0; k < dim; k++) {
1008 x[ii*dim+k] /= len;
1009 }
1010 }
1011
1012 free(mask);
1013}
1014
1015static SparseMatrix shorting_edge_label_nodes(SparseMatrix A, int n_edge_label_nodes, int *edge_label_nodes){
1016 int id = 0;
1017 int *ia = A->ia, *ja = A->ja;
1018
1019 int *mask = gv_calloc(A->m, sizeof(int));
1020
1021 for (size_t i = 0; i < A->m; i++) mask[i] = 1;
1022
1023 for (int i = 0; i < n_edge_label_nodes; i++){
1024 mask[edge_label_nodes[i]] = -1;
1025 }
1026
1027 for (size_t i = 0; i < A->m; i++) {
1028 if (mask[i] > 0) mask[i] = id++;
1029 }
1030
1031 LIST(int) irn = {0};
1032 LIST(int) jcn = {0};
1033 for (size_t i = 0; i < A->m; i++){
1034 if (mask[i] < 0) continue;
1035 for (int j = ia[i]; j < ia[i+1]; j++){
1036 if (mask[ja[j]] >= 0) {
1037 LIST_APPEND(&irn, mask[i]);
1038 LIST_APPEND(&jcn, mask[ja[j]]);
1039 continue;
1040 }
1041 const int ii = ja[j];
1042 for (int jj = ia[ii]; jj < ia[ii+1]; jj++){
1043 if (ja[jj] != (int)i && mask[ja[jj]] >= 0) {
1044 LIST_APPEND(&irn, mask[i]);
1045 LIST_APPEND(&jcn, mask[ja[jj]]);
1046 }
1047 }
1048 }
1049 }
1050
1051 LIST_SYNC(&irn);
1052 LIST_SYNC(&jcn);
1054 LIST_FRONT(&irn),
1055 LIST_FRONT(&jcn), NULL,
1057 sizeof(double));
1058
1059 LIST_FREE(&irn);
1060 LIST_FREE(&jcn);
1061 free(mask);
1062 return B;
1063
1064}
1065
1068 double *label_sizes, double *x,
1069 int n_edge_label_nodes,
1070 int *edge_label_nodes, int *flag) {
1071
1072 int n;
1073 SparseMatrix A = A0, P = NULL;
1074 Multilevel grid, grid0;
1075 double *xc = NULL, *xf = NULL;
1076#ifdef TIME
1077 clock_t cpu;
1078#endif
1079
1080 const spring_electrical_control ctrl0 = *ctrl;
1081
1082#ifdef TIME
1083 cpu = clock();
1084#endif
1085
1086 *flag = 0;
1087 if (!A) return;
1088 n = A->n;
1089 if (n <= 0 || dim <= 0) return;
1090
1091 if (!SparseMatrix_is_symmetric(A, false) || A->type != MATRIX_TYPE_REAL){
1093 } else {
1095 }
1096
1097 /* we first generate a layout discarding (shorting) the edge labels nodes, then assign the edge label nodes at the average of their neighbors */
1099 && n_edge_label_nodes > 0){
1100 SparseMatrix A2;
1101
1102 double *x2 = gv_calloc(A->m * dim, sizeof(double));
1103 A2 = shorting_edge_label_nodes(A, n_edge_label_nodes, edge_label_nodes);
1104 multilevel_spring_electrical_embedding(dim, A2, ctrl, NULL, x2, 0, NULL, flag);
1105
1106 assert(!*flag);
1107 attach_edge_label_coordinates(dim, A, n_edge_label_nodes, edge_label_nodes, x, x2);
1108 remove_overlap(dim, A, x, label_sizes, ctrl->overlap, ctrl->initial_scaling,
1109 ctrl->edge_labeling_scheme, n_edge_label_nodes, edge_label_nodes, A, ctrl->do_shrinking);
1111 free(x2);
1112 if (A != A0) SparseMatrix_delete(A);
1113
1114 return;
1115 }
1116
1117 Multilevel_control mctrl = {.maxlevel = ctrl->multilevels};
1118 grid0 = Multilevel_new(A, mctrl);
1119
1122 xc = x;
1123 } else {
1124 xc = gv_calloc(grid->n * dim, sizeof(double));
1125 }
1126
1127 const bool plg = power_law_graph(A);
1128 if (ctrl->p == AUTOP){
1129 ctrl->p = -1;
1130 if (plg) ctrl->p = -1.8;
1131 }
1132
1133 do {
1134#ifdef DEBUG_PRINT
1135 if (Verbose) {
1136 print_padding(grid->level);
1138 fprintf(stderr, "coarsest level -- %d, n = %d\n", grid->level, grid->n);
1139 } else {
1140 fprintf(stderr, "level -- %d, n = %d\n", grid->level, grid->n);
1141 }
1142 }
1143#endif
1144 if (ctrl->tscheme == QUAD_TREE_NONE){
1145 spring_electrical_embedding_slow(dim, grid->A, ctrl, xc, flag);
1146 } else if (ctrl->tscheme == QUAD_TREE_FAST || (ctrl->tscheme == QUAD_TREE_HYBRID && grid->A->m > QUAD_TREE_HYBRID_SIZE)){
1147 if (ctrl->tscheme == QUAD_TREE_HYBRID && grid->A->m > 10 && Verbose){
1148 fprintf(stderr, "QUAD_TREE_HYBRID, size larger than %d, switch to fast quadtree", QUAD_TREE_HYBRID_SIZE);
1149 }
1150 spring_electrical_embedding_fast(dim, grid->A, ctrl, xc, flag);
1151 } else {
1152 spring_electrical_embedding(dim, grid->A, ctrl, xc, flag);
1153 }
1154 if (Multilevel_is_finest(grid)) break;
1155 if (*flag) {
1156 free(xc);
1157 goto RETURN;
1158 }
1159 P = grid->P;
1160 grid = grid->prev;
1162 xf = x;
1163 } else {
1164 xf = gv_calloc(grid->n * dim, sizeof(double));
1165 }
1166 prolongate(dim, grid->A, P, grid->R, xc, xf, ctrl->K * 0.001);
1167 free(xc);
1168 xc = xf;
1169 ctrl->random_start = false;
1170 ctrl->K = ctrl->K * 0.75;
1171 ctrl->adaptive_cooling = false;
1172 ctrl->step = .1;
1173 } while (grid);
1174
1175#ifdef TIME
1176 if (Verbose)
1177 fprintf(stderr, "layout time %f\n", (double)(clock() - cpu) / CLOCKS_PER_SEC);
1178 cpu = clock();
1179#endif
1180
1181 post_process_smoothing(dim, A, *ctrl, x);
1182
1183 if (Verbose) fprintf(stderr, "ctrl->overlap=%d\n",ctrl->overlap);
1184
1185 /* rotation has to be done before overlap removal, since rotation could induce overlaps */
1186 if (dim == 2){
1187 pcp_rotate(n, dim, x);
1188 }
1189 if (ctrl->rotation != 0) rotate(n, dim, x, ctrl->rotation);
1190
1191
1192 remove_overlap(dim, A, x, label_sizes, ctrl->overlap, ctrl->initial_scaling,
1193 ctrl->edge_labeling_scheme, n_edge_label_nodes, edge_label_nodes, A, ctrl->do_shrinking);
1194
1195 RETURN:
1196 *ctrl = ctrl0;
1197 if (A != A0) SparseMatrix_delete(A);
1198 Multilevel_delete(grid0);
1199 }
void print_padding(int n)
Definition Multilevel.c:245
Multilevel Multilevel_get_coarsest(Multilevel grid)
Definition Multilevel.c:300
void Multilevel_delete(Multilevel grid)
Definition Multilevel.c:41
Multilevel Multilevel_new(SparseMatrix A0, const Multilevel_control ctrl)
Definition Multilevel.c:284
#define Multilevel_is_coarsest(grid)
Definition Multilevel.h:44
#define Multilevel_is_finest(grid)
Definition Multilevel.h:43
void QuadTree_get_repulsive_force(QuadTree qt, double *force, double *x, double bh, double p, double KP, double *counts)
Definition QuadTree.c:283
QuadTree QuadTree_new_from_point_list(int dim, int n, int max_level, double *coord)
Definition QuadTree.c:311
void QuadTree_get_supernodes(QuadTree qt, double bh, double *pt, int nodeid, int *nsuper, double **center, double **supernode_wgts, double **distances, double *counts)
Definition QuadTree.c:95
void QuadTree_delete(QuadTree q)
Definition QuadTree.c:377
SparseMatrix SparseMatrix_symmetrize(SparseMatrix A, bool pattern_symmetric_only)
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, size_t m, int n, int *irn, int *jcn, const void *val, int type, size_t sz)
void SparseMatrix_delete(SparseMatrix A)
SparseMatrix SparseMatrix_get_real_adjacency_matrix_symmetrized(SparseMatrix A)
bool SparseMatrix_has_diagonal(SparseMatrix A)
SparseMatrix SparseMatrix_remove_diagonal(SparseMatrix A)
@ FORMAT_CSR
@ MATRIX_TYPE_REAL
@ MATRIX_TYPE_PATTERN
Dynamically expanding string buffers.
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define MIN(a, b)
Definition arith.h:28
#define M_PI
Definition arith.h:41
#define MAX(a, b)
Definition arith.h:33
API for compacted arrays of booleans.
static bitarray_t bitarray_new(size_t size_bits)
create an array of the given element length
Definition bitarray.h:47
static bool bitarray_get(bitarray_t self, size_t index)
get the value of the given element
Definition bitarray.h:65
static void bitarray_set(bitarray_t *self, size_t index, bool value)
set or clear the value of the given element
Definition bitarray.h:80
static void bitarray_reset(bitarray_t *self)
free underlying resources and leave a bit array empty
Definition bitarray.h:114
#define MINDIST
Definition circular.c:18
#define A(n, t)
Definition expr.h:76
#define F
Definition expr.h:70
static double dist(int dim, double *x, double *y)
double drand(void)
Definition general.c:24
double distance(double *x, int dim, int i, int j)
Definition general.c:116
double distance_cropped(double *x, int dim, int i, int j)
Definition general.c:111
static double len(glCompPoint p)
Definition glutils.c:138
static bool Verbose
Definition gml2gv.c:26
void free(void *)
node NULL
Definition grammar.y:181
@ grid
Definition gvgen.c:34
#define B
Definition hierarchy.c:120
#define D
Definition hierarchy.c:122
type-generic dynamically expanding list
#define LIST_APPEND(list,...)
Definition list.h:124
#define LIST(type)
Definition list.h:55
#define LIST_SIZE(list)
Definition list.h:80
#define LIST_FREE(list)
Definition list.h:373
#define LIST_FRONT(list)
Definition list.h:184
#define LIST_IS_EMPTY(list)
Definition list.h:90
#define LIST_SYNC(list)
Definition list.h:330
#define LIST_GET(list, index)
Definition list.h:159
#define delta
Definition maze.c:136
static const int dim
void remove_overlap(int dim, SparseMatrix A, double *x, double *label_sizes, int ntry, double initial_scaling, int edge_labeling_scheme, int n_constr_nodes, int *constr_nodes, SparseMatrix A_constr, bool do_shrinking)
Definition overlap.c:588
@ ELSCHEME_STRAIGHTLINE_PENALTY2
Definition overlap.h:17
@ ELSCHEME_STRAIGHTLINE_PENALTY
Definition overlap.h:17
void post_process_smoothing(int dim, SparseMatrix A, spring_electrical_control ctrl, double *x)
#define PRISIZE_T
Definition prisize_t.h:25
pointf coord(node_t *n)
Definition utils.c:157
#define alpha
Definition shapes.c:4034
static bool power_law_graph(SparseMatrix A)
void spring_electrical_spring_embedding(int dim, SparseMatrix A0, SparseMatrix D, spring_electrical_control *ctrl, double *x)
static char * smoothings[]
void spring_electrical_control_print(spring_electrical_control ctrl)
void multilevel_spring_electrical_embedding(int dim, SparseMatrix A0, spring_electrical_control *ctrl, double *label_sizes, double *x, int n_edge_label_nodes, int *edge_label_nodes, int *flag)
static double update_step(bool adaptive_cooling, double step, double Fnorm, double Fnorm0)
@ OPT_INIT
@ OPT_DOWN
spring_electrical_control spring_electrical_control_new(void)
static const int quadtree_size
cut off size above which quadtree approximation is used
static SparseMatrix shorting_edge_label_nodes(SparseMatrix A, int n_edge_label_nodes, int *edge_label_nodes)
static int oned_optimizer_get(const oned_optimizer opt)
static void prolongate(int dim, SparseMatrix A, SparseMatrix P, SparseMatrix R, double *x, double *y, double delta)
static void spring_electrical_embedding_fast(int dim, SparseMatrix A0, spring_electrical_control *ctrl, double *x, int *flag)
static void beautify_leaves(int dim, SparseMatrix A, double *x)
static oned_optimizer oned_optimizer_new(int i)
static char * tschemes[]
static void attach_edge_label_coordinates(int dim, SparseMatrix A, int n_edge_label_nodes, int *edge_label_nodes, double *x, double *x2)
static const double tol
static const double cool
static const double C
double average_edge_length(SparseMatrix A, int dim, double *coord)
static void spring_electrical_embedding(int dim, SparseMatrix A0, spring_electrical_control *ctrl, double *x, int *flag)
static void oned_optimizer_train(oned_optimizer *opt, double work)
void pcp_rotate(int n, int dim, double *x)
static const double bh
static void rotate(int n, int dim, double *x, double angle)
static void set_leaves(double *x, int dim, double dist, double ang, int i, int j)
static void spring_electrical_embedding_slow(int dim, SparseMatrix A0, spring_electrical_control *ctrl, double *x, int *flag)
#define node_degree(i)
static void interpolate_coord(int dim, SparseMatrix A, double *x)
@ SMOOTHING_NONE
#define AUTOP
@ ERROR_NOT_SQUARE_MATRIX
@ QUAD_TREE_HYBRID_SIZE
@ QUAD_TREE_FAST
@ QUAD_TREE_HYBRID
@ QUAD_TREE_NONE
#define RETURN(v)
Definition strmatch.c:146
size_t m
row dimension
double work[MAX_I+1]
bool random_start
whether to apply SE from a random layout, or from existing layout
double p
a negative real number default to -1. repulsive force = distáµ–
static point center(point vertex[], size_t n)
graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t