Graphviz 16.0.1~dev.20260815.2250
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#ifdef TIME
348 start = clock();
349#endif
350 QuadTree_delete(qt);
351#ifdef TIME
352 end = clock();
353 qtree_new_cpu += (double)(end - start) / CLOCKS_PER_SEC;
354#endif
355
356 oned_optimizer_train(&qtree_level_optimizer,
357 counts[0] + 0.85 * counts[1] + 3.3 * counts[2]);
358
359 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
360 } while (step > tol && iter < maxiter);
361
362#ifdef DEBUG_PRINT
363 if (Verbose) {
364 fprintf(stderr, "\n iter = %d, step = %f Fnorm = %f nz = %" PRISIZE_T
365 " K = %f ", iter, step, Fnorm, A->nz, K);
366 }
367#endif
368
369 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
370
371#ifdef TIME
372 total_cpu += (double)(clock() - start0) / CLOCKS_PER_SEC;
373 if (Verbose) fprintf(stderr, "\n time for qtree = %f, qtree_force = %f, total cpu = %f\n",qtree_new_cpu, qtree_cpu, total_cpu);
374#endif
375
376
377 RETURN:
378 ctrl->max_qtree_level = max_qtree_level;
379
380 if (A != A0) SparseMatrix_delete(A);
381 free(force);
382}
383
386 double *x, int *flag) {
387 /* 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. */
388 /* x is a point to a 1D array, x[i*dim+j] gives the coordinate of the i-th node at dimension j. */
389 SparseMatrix A = A0;
390 int n;
391 int i, j, k;
392 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
393 int *ia = NULL, *ja = NULL;
394 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
395 int iter = 0;
396 const bool adaptive_cooling = ctrl->adaptive_cooling;
397 double *force;
398#ifdef TIME
399 clock_t start, end, start0, start2;
400 double total_cpu = 0;
401 start0 = clock();
402#endif
403
404 fprintf(stderr,"spring_electrical_embedding_slow");
405 if (!A || maxiter <= 0) return;
406
407 const size_t m = A->m;
408 n = A->n;
409 if (n <= 0 || dim <= 0) return;
410 force = gv_calloc(n *dim, sizeof(double));
411
412 *flag = 0;
413 if (m != (size_t)n) {
415 goto RETURN;
416 }
417 assert(A->format == FORMAT_CSR);
418 A = SparseMatrix_symmetrize(A, true);
419 ia = A->ia;
420 ja = A->ja;
421
422 if (ctrl->random_start){
423 srand(ctrl->random_seed);
424 for (i = 0; i < dim*n; i++) x[i] = drand();
425 }
426 if (K < 0){
427 ctrl->K = K = average_edge_length(A, dim, x);
428 }
429 if (p >= 0) ctrl->p = p = -1;
430 KP = pow(K, 1 - p);
431 CRK = pow(C, (2.-p)/3.)/K;
432
433 f = gv_calloc(dim, sizeof(double));
434 do {
435 for (i = 0; i < dim*n; i++) force[i] = 0;
436
437 iter++;
438 Fnorm0 = Fnorm;
439 Fnorm = 0.;
440
441#ifdef TIME
442 start2 = clock();
443#endif
444
445
446 for (i = 0; i < n; i++){
447 for (k = 0; k < dim; k++) f[k] = 0.;
448 /* repulsive force K^(1 - p)/||x_i-x_j||^(1 - p) (x_i - x_j) */
449 for (j = 0; j < n; j++){
450 if (j == i) continue;
451 dist = distance_cropped(x, dim, i, j);
452 for (k = 0; k < dim; k++){
453 f[k] += KP*(x[i*dim+k] - x[j*dim+k])/pow(dist, 1.- p);
454 }
455 }
456 for (k = 0; k < dim; k++) force[i*dim+k] += f[k];
457 }
458
459
460
461 for (i = 0; i < n; i++){
462 for (k = 0; k < dim; k++) f[k] = 0.;
463 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
464 for (j = ia[i]; j < ia[i+1]; j++){
465 if (ja[j] == i) continue;
466 dist = distance(x, dim, i, ja[j]);
467 for (k = 0; k < dim; k++){
468 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
469 }
470 }
471 for (k = 0; k < dim; k++) force[i*dim+k] += f[k];
472 }
473
474
475
476 for (i = 0; i < n; i++){
477 /* normalize force */
478 for (k = 0; k < dim; k++) f[k] = force[i*dim+k];
479
480 F = 0.;
481 for (k = 0; k < dim; k++) F += f[k]*f[k];
482 F = sqrt(F);
483 Fnorm += F;
484
485 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
486
487 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
488
489 }/* done vertex i */
490
491 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
492 } while (step > tol && iter < maxiter);
493
494#ifdef DEBUG_PRINT
495 if (Verbose) {
496 fprintf(stderr, "iter = %d, step = %f Fnorm = %f nsuper = 0 nz = %d K = %f ",iter, step, Fnorm, A->nz,K);
497 }
498#endif
499
500 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
501
502#ifdef TIME
503 total_cpu += (double)(clock() - start0) / CLOCKS_PER_SEC;
504 if (Verbose) fprintf(stderr, "time for supernode = 0, total cpu = %f\n", total_cpu);
505#endif
506
507 RETURN:
508 if (A != A0) SparseMatrix_delete(A);
509 free(f);
510 free(force);
511}
512
515 double *x, int *flag) {
516 /* x is a point to a 1D array, x[i*dim+j] gives the coordinate of the i-th node at dimension j. */
517 SparseMatrix A = A0;
518 int n;
519 int i, j, k;
520 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
521 int *ia = NULL, *ja = NULL;
522 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
523 int iter = 0;
524 const bool adaptive_cooling = ctrl->adaptive_cooling;
525 bool USE_QT = false;
526 int nsuper = 0;
527 double *center = NULL, *supernode_wgts = NULL, *distances = NULL, nsuper_avg, counts = 0, counts_avg = 0;
528#ifdef TIME
529 clock_t start, end, start0, start2;
530 double qtree_cpu = 0;
531 double total_cpu = 0;
532 start0 = clock();
533#endif
534 int max_qtree_level = ctrl->max_qtree_level;
535 oned_optimizer qtree_level_optimizer = {0};
536
537 if (!A || maxiter <= 0) return;
538
539 const size_t m = A->m;
540 n = A->n;
541 if (n <= 0 || dim <= 0) return;
542
543 if (n >= quadtree_size) {
544 USE_QT = true;
545 qtree_level_optimizer = oned_optimizer_new(max_qtree_level);
546 }
547 *flag = 0;
548 if (m != (size_t)n) {
550 goto RETURN;
551 }
552 assert(A->format == FORMAT_CSR);
553 A = SparseMatrix_symmetrize(A, true);
554 ia = A->ia;
555 ja = A->ja;
556
557 if (ctrl->random_start){
558 srand(ctrl->random_seed);
559 for (i = 0; i < dim*n; i++) x[i] = drand();
560 }
561 if (K < 0){
562 ctrl->K = K = average_edge_length(A, dim, x);
563 }
564 if (p >= 0) ctrl->p = p = -1;
565 KP = pow(K, 1 - p);
566 CRK = pow(C, (2.-p)/3.)/K;
567
568 f = gv_calloc(dim, sizeof(double));
569 do {
570
571 iter++;
572 Fnorm0 = Fnorm;
573 Fnorm = 0.;
574 nsuper_avg = 0;
575 counts_avg = 0;
576
577 QuadTree qt = NULL;
578 if (USE_QT) {
579
580 max_qtree_level = oned_optimizer_get(qtree_level_optimizer);
581 qt = QuadTree_new_from_point_list(dim, n, max_qtree_level, x);
582
583
584 }
585#ifdef TIME
586 start2 = clock();
587#endif
588
589 for (i = 0; i < n; i++){
590 for (k = 0; k < dim; k++) f[k] = 0.;
591 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
592 for (j = ia[i]; j < ia[i+1]; j++){
593 if (ja[j] == i) continue;
594 dist = distance(x, dim, i, ja[j]);
595 for (k = 0; k < dim; k++){
596 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
597 }
598 }
599
600 /* repulsive force K^(1 - p)/||x_i-x_j||^(1 - p) (x_i - x_j) */
601 if (USE_QT){
602#ifdef TIME
603 start = clock();
604#endif
605 QuadTree_get_supernodes(qt, bh, &(x[dim*i]), i, &nsuper,
606 &center, &supernode_wgts, &distances, &counts);
607
608#ifdef TIME
609 end = clock();
610 qtree_cpu += (double)(end - start) / CLOCKS_PER_SEC;
611#endif
612 counts_avg += counts;
613 nsuper_avg += nsuper;
614 for (j = 0; j < nsuper; j++){
615 dist = MAX(distances[j], MINDIST);
616 for (k = 0; k < dim; k++){
617 f[k] += supernode_wgts[j]*KP*(x[i*dim+k] - center[j*dim+k])/pow(dist, 1.- p);
618 }
619 }
620 } else {
621 for (j = 0; j < n; j++){
622 if (j == i) continue;
623 dist = distance_cropped(x, dim, i, j);
624 for (k = 0; k < dim; k++){
625 f[k] += KP*(x[i*dim+k] - x[j*dim+k])/pow(dist, 1.- p);
626 }
627 }
628 }
629
630 /* normalize force */
631 F = 0.;
632 for (k = 0; k < dim; k++) F += f[k]*f[k];
633 F = sqrt(F);
634 Fnorm += F;
635
636 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
637
638 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
639
640 }/* done vertex i */
641
642 if (qt) {
643 QuadTree_delete(qt);
644 nsuper_avg /= n;
645 counts_avg /= n;
646 oned_optimizer_train(&qtree_level_optimizer, 5 * nsuper_avg + counts_avg);
647 }
648
649 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
650 } while (step > tol && iter < maxiter);
651
652#ifdef DEBUG_PRINT
653 if (Verbose) {
654 if (USE_QT){
655 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);
656 } else {
657 fprintf(stderr, "iter = %d, step = %f Fnorm = %f nsuper = %d nz = %d K = %f ",iter, step, Fnorm, (int) nsuper_avg,A->nz,K);
658 }
659 }
660#endif
661
662 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
663
664#ifdef TIME
665 total_cpu += (double)(clock() - start0) / CLOCKS_PER_SEC;
666 if (Verbose) fprintf(stderr, "time for supernode = %f, total cpu = %f\n",qtree_cpu, total_cpu);
667#endif
668
669 RETURN:
670 if (USE_QT) {
671 ctrl->max_qtree_level = max_qtree_level;
672 }
673 if (A != A0) SparseMatrix_delete(A);
674 free(f);
675 free(center);
676 free(supernode_wgts);
677 free(distances);
678}
679
682 double *x) {
683 /* 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
684 introduce force due to spring length
685 */
686 SparseMatrix A = A0;
687 int n;
688 int i, j, k;
689 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
690 int *ia = NULL, *ja = NULL;
691 int *id = NULL, *jd = NULL;
692 double *d;
693 double *xold = NULL;
694 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
695 int iter = 0;
696 const bool adaptive_cooling = ctrl->adaptive_cooling;
697 bool USE_QT = false;
698 int nsuper = 0;
699 double *center = NULL, *supernode_wgts = NULL, *distances = NULL, counts = 0;
700 int max_qtree_level = 10;
701
702 if (!A || maxiter <= 0) return;
703 n = A->n;
704 if (n <= 0 || dim <= 0) return;
705
706 if (n >= quadtree_size) {
707 USE_QT = true;
708 }
709 assert(A->m == (size_t)n);
710 assert(A->format == FORMAT_CSR);
711 A = SparseMatrix_symmetrize(A, true);
712 ia = A->ia;
713 ja = A->ja;
714 id = D->ia;
715 jd = D->ja;
716 d = D->a;
717
718 if (ctrl->random_start){
719 srand(ctrl->random_seed);
720 for (i = 0; i < dim*n; i++) x[i] = drand();
721 }
722 if (K < 0){
723 ctrl->K = K = average_edge_length(A, dim, x);
724 }
725 if (p >= 0) ctrl->p = p = -1;
726 KP = pow(K, 1 - p);
727 CRK = pow(C, (2.-p)/3.)/K;
728
729 f = gv_calloc(dim, sizeof(double));
730 xold = gv_calloc(dim * n, sizeof(double));
731 do {
732 iter++;
733 memcpy(xold, x, sizeof(double)*dim*n);
734 Fnorm0 = Fnorm;
735 Fnorm = 0.;
736
737 QuadTree qt = NULL;
738 if (USE_QT) {
739 qt = QuadTree_new_from_point_list(dim, n, max_qtree_level, x);
740 }
741
742 for (i = 0; i < n; i++){
743 for (k = 0; k < dim; k++) f[k] = 0.;
744 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
745
746 for (j = ia[i]; j < ia[i+1]; j++){
747 if (ja[j] == i) continue;
748 dist = distance(x, dim, i, ja[j]);
749 for (k = 0; k < dim; k++){
750 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
751 }
752 }
753
754 for (j = id[i]; j < id[i+1]; j++){
755 if (jd[j] == i) continue;
756 dist = distance_cropped(x, dim, i, jd[j]);
757 for (k = 0; k < dim; k++){
758 if (dist < d[j]){
759 f[k] += 0.2*CRK*(x[i*dim+k] - x[jd[j]*dim+k])*(dist - d[j])*(dist - d[j])/dist;
760 } else {
761 f[k] -= 0.2*CRK*(x[i*dim+k] - x[jd[j]*dim+k])*(dist - d[j])*(dist - d[j])/dist;
762 }
763 /* f[k] -= 0.2*CRK*(x[i*dim+k] - x[jd[j]*dim+k])*(dist - d[j]);*/
764 }
765 }
766
767 /* repulsive force K^(1 - p)/||x_i-x_j||^(1 - p) (x_i - x_j) */
768 if (USE_QT){
769 QuadTree_get_supernodes(qt, bh, &x[dim * i], i, &nsuper,
770 &center, &supernode_wgts, &distances, &counts);
771 for (j = 0; j < nsuper; j++){
772 dist = MAX(distances[j], MINDIST);
773 for (k = 0; k < dim; k++){
774 f[k] += supernode_wgts[j]*KP*(x[i*dim+k] - center[j*dim+k])/pow(dist, 1.- p);
775 }
776 }
777 } else {
778 for (j = 0; j < n; j++){
779 if (j == i) continue;
780 dist = distance_cropped(x, dim, i, j);
781 for (k = 0; k < dim; k++){
782 f[k] += KP*(x[i*dim+k] - x[j*dim+k])/pow(dist, 1.- p);
783 }
784 }
785 }
786
787 /* normalize force */
788 F = 0.;
789 for (k = 0; k < dim; k++) F += f[k]*f[k];
790 F = sqrt(F);
791 Fnorm += F;
792
793 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
794
795 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
796
797 }/* done vertex i */
798
799 if (qt) QuadTree_delete(qt);
800
801 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
802 } while (step > tol && iter < maxiter);
803
804 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
805
806 free(xold);
807 if (A != A0) SparseMatrix_delete(A);
808 free(f);
809 free(center);
810 free(supernode_wgts);
811 free(distances);
812}
813
814static void interpolate_coord(int dim, SparseMatrix A, double *x) {
815 int j, k, *ia = A->ia, *ja = A->ja, nz;
816 double alpha = 0.5, beta;
817
818 double *y = gv_calloc(dim, sizeof(double));
819 for (size_t i = 0; i < A->m; i++){
820 for (k = 0; k < dim; k++) y[k] = 0;
821 nz = 0;
822 for (j = ia[i]; j < ia[i+1]; j++){
823 if (ja[j] == (int)i) continue;
824 nz++;
825 for (k = 0; k < dim; k++){
826 y[k] += x[ja[j]*dim + k];
827 }
828 }
829 if (nz > 0){
830 beta = (1-alpha)/nz;
831 for (k = 0; k < dim; k++) x[(int)i*dim+k] = alpha*x[(int)i*dim+k] + beta*y[k];
832 }
833 }
834
835 free(y);
836}
837static void prolongate(int dim, SparseMatrix A, SparseMatrix P, SparseMatrix R, double *x, double *y, double delta){
838 int *ia, *ja, j, k;
840
842 const size_t nc = R->m;
843 ia = R->ia;
844 ja = R->ja;
845 for (size_t i = 0; i < nc; i++){
846 for (j = ia[i]+1; j < ia[i+1]; j++){
847 for (k = 0; k < dim; k++){
848 y[ja[j]*dim + k] += delta*(drand() - 0.5);
849 }
850 }
851 }
852}
853
855 int max = 0, *ia = A->ia, *ja = A->ja, j, deg;
856 bool res = false;
857 const size_t m = A->m;
858 int *mask = gv_calloc(m + 1, sizeof(int));
859
860 for (size_t i = 0; i < m + 1; i++){
861 mask[i] = 0;
862 }
863
864 for (size_t i = 0; i < m; i++){
865 deg = 0;
866 for (j = ia[i]; j < ia[i+1]; j++){
867 if ((int)i == ja[j]) continue;
868 deg++;
869 }
870 mask[deg]++;
871 max = MAX(max, mask[deg]);
872 }
873 if (mask[1] > 0.8*max && mask[1] > 0.3 * (double)m) res = true;
874 free(mask);
875 return res;
876}
877
878static void pcp_rotate(int n, int dim, double *x) {
879 int i, k,l;
880 double y[4], axis[2], center[2], dist, x0, x1;
881
882 assert(dim == 2);
883 for (i = 0; i < dim*dim; i++) y[i] = 0;
884 for (i = 0; i < dim; i++) center[i] = 0;
885 for (i = 0; i < n; i++){
886 for (k = 0; k < dim; k++){
887 center[k] += x[i*dim+k];
888 }
889 }
890 for (i = 0; i < dim; i++) center[i] /= n;
891 for (i = 0; i < n; i++){
892 for (k = 0; k < dim; k++){
893 x[dim*i+k] = x[dim*i+k] - center[k];
894 }
895 }
896
897 for (i = 0; i < n; i++){
898 for (k = 0; k < dim; k++){
899 for (l = 0; l < dim; l++){
900 y[dim*k+l] += x[i*dim+k]*x[i*dim+l];
901 }
902 }
903 }
904 if (y[1] == 0) {
905 axis[0] = 0; axis[1] = 1;
906 } else {
907 /* Eigensystem[{{x0, x1}, {x1, x3}}] =
908 {{(x0 + x3 - Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/2,
909 (x0 + x3 + Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/2},
910 {{-(-x0 + x3 + Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/(2*x1), 1},
911 {-(-x0 + x3 - Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/(2*x1), 1}}}
912 */
913 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]);
914 axis[1] = 1;
915 }
916 dist = sqrt(1+axis[0]*axis[0]);
917 axis[0] = axis[0]/dist;
918 axis[1] = axis[1]/dist;
919 for (i = 0; i < n; i++){
920 x0 = x[dim*i]*axis[0]+x[dim*i+1]*axis[1];
921 x1 = -x[dim*i]*axis[1]+x[dim*i+1]*axis[0];
922 x[dim*i] = x0;
923 x[dim*i + 1] = x1;
924
925 }
926
927
928}
929
930static void rotate(int n, int dim, double *x, double angle){
931 int i, k;
932 double axis[2], center[2], x0, x1;
933 double radian = 3.14159/180;
934
935 assert(dim == 2);
936 for (i = 0; i < dim; i++) center[i] = 0;
937 for (i = 0; i < n; i++){
938 for (k = 0; k < dim; k++){
939 center[k] += x[i*dim+k];
940 }
941 }
942 for (i = 0; i < dim; i++) center[i] /= n;
943 for (i = 0; i < n; i++){
944 for (k = 0; k < dim; k++){
945 x[dim*i+k] = x[dim*i+k] - center[k];
946 }
947 }
948 axis[0] = cos(-angle*radian);
949 axis[1] = sin(-angle*radian);
950 for (i = 0; i < n; i++){
951 x0 = x[dim*i]*axis[0]+x[dim*i+1]*axis[1];
952 x1 = -x[dim*i]*axis[1]+x[dim*i+1]*axis[0];
953 x[dim*i] = x0;
954 x[dim*i + 1] = x1;
955 }
956
957
958}
959
960static void attach_edge_label_coordinates(int dim, SparseMatrix A, int n_edge_label_nodes, int *edge_label_nodes, double *x, double *x2){
961 int ii, j, k;
962 int nnodes = 0;
963 double len;
964
965 int *mask = gv_calloc(A->m, sizeof(int));
966
967 for (size_t i = 0; i < A->m; i++) mask[i] = 1;
968 for (int i = 0; i < n_edge_label_nodes; i++) {
969 if (edge_label_nodes[i] >= 0 && (size_t)edge_label_nodes[i] < A->m) mask[edge_label_nodes[i]] = -1;
970 }
971
972 for (size_t i = 0; i < A->m; i++) {
973 if (mask[i] >= 0) mask[i] = nnodes++;
974 }
975
976
977 for (size_t i = 0; i < A->m; i++){
978 if (mask[i] >= 0){
979 for (k = 0; k < dim; k++) x[(int)i * dim + k] = x2[mask[i]*dim+k];
980 }
981 }
982
983 for (int i = 0; i < n_edge_label_nodes; i++){
984 ii = edge_label_nodes[i];
985 len = A->ia[ii+1] - A->ia[ii];
986 assert(len >= 2); /* should just be 2 */
987 assert(mask[ii] < 0);
988 for (k = 0; k < dim; k++) {
989 x[ii*dim+k] = 0;
990 }
991 for (j = A->ia[ii]; j < A->ia[ii+1]; j++){
992 for (k = 0; k < dim; k++){
993 x[ii * dim + k] += x[A->ja[j] * dim + k];
994 }
995 }
996 for (k = 0; k < dim; k++) {
997 x[ii*dim+k] /= len;
998 }
999 }
1000
1001 free(mask);
1002}
1003
1004static SparseMatrix shorting_edge_label_nodes(SparseMatrix A, int n_edge_label_nodes, int *edge_label_nodes){
1005 int id = 0;
1006 int *ia = A->ia, *ja = A->ja;
1007
1008 int *mask = gv_calloc(A->m, sizeof(int));
1009
1010 for (size_t i = 0; i < A->m; i++) mask[i] = 1;
1011
1012 for (int i = 0; i < n_edge_label_nodes; i++){
1013 mask[edge_label_nodes[i]] = -1;
1014 }
1015
1016 for (size_t i = 0; i < A->m; i++) {
1017 if (mask[i] > 0) mask[i] = id++;
1018 }
1019
1020 LIST(int) irn = {0};
1021 LIST(int) jcn = {0};
1022 for (size_t i = 0; i < A->m; i++){
1023 if (mask[i] < 0) continue;
1024 for (int j = ia[i]; j < ia[i+1]; j++){
1025 if (mask[ja[j]] >= 0) {
1026 LIST_APPEND(&irn, mask[i]);
1027 LIST_APPEND(&jcn, mask[ja[j]]);
1028 continue;
1029 }
1030 const int ii = ja[j];
1031 for (int jj = ia[ii]; jj < ia[ii+1]; jj++){
1032 if (ja[jj] != (int)i && mask[ja[jj]] >= 0) {
1033 LIST_APPEND(&irn, mask[i]);
1034 LIST_APPEND(&jcn, mask[ja[jj]]);
1035 }
1036 }
1037 }
1038 }
1039
1040 LIST_SYNC(&irn);
1041 LIST_SYNC(&jcn);
1043 LIST_FRONT(&irn),
1044 LIST_FRONT(&jcn), NULL,
1046 sizeof(double));
1047
1048 LIST_FREE(&irn);
1049 LIST_FREE(&jcn);
1050 free(mask);
1051 return B;
1052
1053}
1054
1057 double *label_sizes, double *x,
1058 int n_edge_label_nodes,
1059 int *edge_label_nodes, int *flag) {
1060
1061 int n;
1062 SparseMatrix A = A0, P = NULL;
1063 Multilevel grid, grid0;
1064 double *xc = NULL, *xf = NULL;
1065#ifdef TIME
1066 clock_t cpu;
1067#endif
1068
1069 const spring_electrical_control ctrl0 = *ctrl;
1070
1071#ifdef TIME
1072 cpu = clock();
1073#endif
1074
1075 *flag = 0;
1076 if (!A) return;
1077 n = A->n;
1078 if (n <= 0 || dim <= 0) return;
1079
1080 if (!SparseMatrix_is_symmetric(A, false) || A->type != MATRIX_TYPE_REAL){
1082 } else {
1084 }
1085
1086 /* we first generate a layout discarding (shorting) the edge labels nodes, then assign the edge label nodes at the average of their neighbors */
1088 && n_edge_label_nodes > 0){
1089 SparseMatrix A2;
1090
1091 double *x2 = gv_calloc(A->m * dim, sizeof(double));
1092 A2 = shorting_edge_label_nodes(A, n_edge_label_nodes, edge_label_nodes);
1093 multilevel_spring_electrical_embedding(dim, A2, ctrl, NULL, x2, 0, NULL, flag);
1094
1095 assert(!*flag);
1096 attach_edge_label_coordinates(dim, A, n_edge_label_nodes, edge_label_nodes, x, x2);
1097 remove_overlap(dim, A, x, label_sizes, ctrl->overlap, ctrl->initial_scaling,
1098 ctrl->edge_labeling_scheme, n_edge_label_nodes, edge_label_nodes, A, ctrl->do_shrinking);
1100 free(x2);
1101 if (A != A0) SparseMatrix_delete(A);
1102
1103 return;
1104 }
1105
1106 Multilevel_control mctrl = {.maxlevel = ctrl->multilevels};
1107 grid0 = Multilevel_new(A, mctrl);
1108
1111 xc = x;
1112 } else {
1113 xc = gv_calloc(grid->n * dim, sizeof(double));
1114 }
1115
1116 const bool plg = power_law_graph(A);
1117 if (ctrl->p == AUTOP){
1118 ctrl->p = -1;
1119 if (plg) ctrl->p = -1.8;
1120 }
1121
1122 do {
1123#ifdef DEBUG_PRINT
1124 if (Verbose) {
1125 print_padding(grid->level);
1127 fprintf(stderr, "coarsest level -- %d, n = %d\n", grid->level, grid->n);
1128 } else {
1129 fprintf(stderr, "level -- %d, n = %d\n", grid->level, grid->n);
1130 }
1131 }
1132#endif
1133 if (ctrl->tscheme == QUAD_TREE_NONE){
1134 spring_electrical_embedding_slow(dim, grid->A, ctrl, xc, flag);
1135 } else if (ctrl->tscheme == QUAD_TREE_FAST || (ctrl->tscheme == QUAD_TREE_HYBRID && grid->A->m > QUAD_TREE_HYBRID_SIZE)){
1136 if (ctrl->tscheme == QUAD_TREE_HYBRID && grid->A->m > 10 && Verbose){
1137 fprintf(stderr, "QUAD_TREE_HYBRID, size larger than %d, switch to fast quadtree", QUAD_TREE_HYBRID_SIZE);
1138 }
1139 spring_electrical_embedding_fast(dim, grid->A, ctrl, xc, flag);
1140 } else {
1141 spring_electrical_embedding(dim, grid->A, ctrl, xc, flag);
1142 }
1143 if (Multilevel_is_finest(grid)) break;
1144 if (*flag) {
1145 free(xc);
1146 goto RETURN;
1147 }
1148 P = grid->P;
1149 grid = grid->prev;
1151 xf = x;
1152 } else {
1153 xf = gv_calloc(grid->n * dim, sizeof(double));
1154 }
1155 prolongate(dim, grid->A, P, grid->R, xc, xf, ctrl->K * 0.001);
1156 free(xc);
1157 xc = xf;
1158 ctrl->random_start = false;
1159 ctrl->K = ctrl->K * 0.75;
1160 ctrl->adaptive_cooling = false;
1161 ctrl->step = .1;
1162 } while (grid);
1163
1164#ifdef TIME
1165 if (Verbose)
1166 fprintf(stderr, "layout time %f\n", (double)(clock() - cpu) / CLOCKS_PER_SEC);
1167 cpu = clock();
1168#endif
1169
1170 post_process_smoothing(dim, A, *ctrl, x);
1171
1172 if (Verbose) fprintf(stderr, "ctrl->overlap=%d\n",ctrl->overlap);
1173
1174 /* rotation has to be done before overlap removal, since rotation could induce overlaps */
1175 if (dim == 2){
1176 pcp_rotate(n, dim, x);
1177 }
1178 if (ctrl->rotation != 0) rotate(n, dim, x, ctrl->rotation);
1179
1180
1181 remove_overlap(dim, A, x, label_sizes, ctrl->overlap, ctrl->initial_scaling,
1182 ctrl->edge_labeling_scheme, n_edge_label_nodes, edge_label_nodes, A, ctrl->do_shrinking);
1183
1184 RETURN:
1185 *ctrl = ctrl0;
1186 if (A != A0) SparseMatrix_delete(A);
1187 Multilevel_delete(grid0);
1188 }
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)
@ MATRIX_TYPE_REAL
@ MATRIX_TYPE_PATTERN
@ FORMAT_CSR
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:25
double distance(double *x, int dim, int i, int j)
Definition general.c:100
double distance_cropped(double *x, int dim, int i, int j)
Definition general.c:95
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:350
#define LIST_FRONT(list)
Definition list.h:184
#define LIST_IS_EMPTY(list)
Definition list.h:90
#define LIST_SYNC(list)
Definition list.h:307
#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 void pcp_rotate(int n, int dim, double *x)
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)
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