Graphviz 14.1.2~dev.20251216.0548
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 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#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, i, j, k;
156 assert(SparseMatrix_is_symmetric(A, true));
157
158 if (ia[A->m] == 0) return 1;
159 for (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*i+k] - coord[dim*ja[j]])*(coord[dim*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 m = A->m, i, j, *ia = A->ia, *ja = A->ja;
197 int p;
198 double dist;
199 double step;
200
202
203 bitarray_t checked = bitarray_new(m);
204
205 for (i = 0; i < m; i++){
206 if (ia[i+1] - ia[i] != 1) continue;
207 if (bitarray_get(checked, i)) continue;
208 p = ja[ia[i]];
209 if (!bitarray_get(checked, p)) {
210 bitarray_set(&checked, p, true);
211 dist = 0;
212 LIST(int) leaves = {0};
213 for (j = ia[p]; j < ia[p+1]; j++){
214 if (node_degree(ja[j]) == 1){
215 bitarray_set(&checked, ja[j], true);
216 dist += distance(x, dim, p, ja[j]);
217 LIST_APPEND(&leaves, ja[j]);
218 }
219 }
220 assert(!LIST_IS_EMPTY(&leaves));
221 dist /= (double)LIST_SIZE(&leaves);
222 double ang1 = 0;
223 double ang2 = 2 * M_PI;
224 const double pad = 0.1; // fudge factor to account for the size and
225 // placement of the nodes themselves
226 ang1 += pad;
227 ang2 -= pad;
228 assert(ang2 >= ang1);
229 step = 0.;
230 if (LIST_SIZE(&leaves) > 1) step = (ang2 - ang1) / (double)LIST_SIZE(&leaves);
231 for (size_t k = 0; k < LIST_SIZE(&leaves); k++) {
232 set_leaves(x, dim, dist, ang1, p, LIST_GET(&leaves, k));
233 ang1 += step;
234 }
235 LIST_FREE(&leaves);
236 }
237 }
238
239 bitarray_reset(&checked);
240}
241
244 double *x, int *flag) {
245 /* x is a point to a 1D array, x[i*dim+j] gives the coordinate of the i-th node at dimension j. */
246 SparseMatrix A = A0;
247 int m, n;
248 int i, j, k;
249 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
250 int *ia = NULL, *ja = NULL;
251 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
252 int iter = 0;
253 const bool adaptive_cooling = ctrl->adaptive_cooling;
254 double counts[4], *force = NULL;
255#ifdef TIME
256 clock_t start, end, start0;
257 double qtree_cpu = 0, qtree_new_cpu = 0;
258 double total_cpu = 0;
259 start0 = clock();
260#endif
261 int max_qtree_level = ctrl->max_qtree_level;
262
263 if (!A || maxiter <= 0) return;
264
265 m = A->m, n = A->n;
266 if (n <= 0 || dim <= 0) return;
267
268 oned_optimizer qtree_level_optimizer = oned_optimizer_new(max_qtree_level);
269
270 *flag = 0;
271 if (m != n) {
273 goto RETURN;
274 }
275 assert(A->format == FORMAT_CSR);
276 A = SparseMatrix_symmetrize(A, true);
277 ia = A->ia;
278 ja = A->ja;
279
280 if (ctrl->random_start){
281 srand(ctrl->random_seed);
282 for (i = 0; i < dim*n; i++) x[i] = drand();
283 }
284 if (K < 0){
285 ctrl->K = K = average_edge_length(A, dim, x);
286 }
287 if (p >= 0) ctrl->p = p = -1;
288 KP = pow(K, 1 - p);
289 CRK = pow(C, (2.-p)/3.)/K;
290
291 force = gv_calloc(dim * n, sizeof(double));
292
293 do {
294 iter++;
295 Fnorm0 = Fnorm;
296 Fnorm = 0.;
297
298 max_qtree_level = oned_optimizer_get(qtree_level_optimizer);
299
300#ifdef TIME
301 start = clock();
302#endif
303 QuadTree qt = QuadTree_new_from_point_list(dim, n, max_qtree_level, x);
304
305#ifdef TIME
306 qtree_new_cpu += (double)(clock() - start) / CLOCKS_PER_SEC;
307#endif
308
309 /* repulsive force */
310#ifdef TIME
311 start = clock();
312#endif
313
314 QuadTree_get_repulsive_force(qt, force, x, bh, p, KP, counts);
315
316#ifdef TIME
317 end = clock();
318 qtree_cpu += (double)(end - start) / CLOCKS_PER_SEC;
319#endif
320
321 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
322 for (i = 0; i < n; i++){
323 f = &(force[i*dim]);
324 for (j = ia[i]; j < ia[i+1]; j++){
325 if (ja[j] == i) continue;
326 dist = distance(x, dim, i, ja[j]);
327 for (k = 0; k < dim; k++){
328 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
329 }
330 }
331 }
332
333
334 /* move */
335 for (i = 0; i < n; i++){
336 f = &force[i*dim];
337 F = 0.;
338 for (k = 0; k < dim; k++) F += f[k]*f[k];
339 F = sqrt(F);
340 Fnorm += F;
341 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
342 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
343 }/* done vertex i */
344
345
346
347 if (qt) {
348#ifdef TIME
349 start = clock();
350#endif
351 QuadTree_delete(qt);
352#ifdef TIME
353 end = clock();
354 qtree_new_cpu += (double)(end - start) / CLOCKS_PER_SEC;
355#endif
356
357 oned_optimizer_train(&qtree_level_optimizer,
358 counts[0] + 0.85 * counts[1] + 3.3 * counts[2]);
359 } else {
360 if (Verbose) {
361 fprintf(stderr,
362 "\r iter = %d, step = %f Fnorm = %f nz = %"
363 PRISIZE_T " K = %f ", iter,
364 step, Fnorm, A->nz, K);
365 }
366 }
367
368 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
369 } while (step > tol && iter < maxiter);
370
371#ifdef DEBUG_PRINT
372 if (Verbose) {
373 fprintf(stderr, "\n iter = %d, step = %f Fnorm = %f nz = %" PRISIZE_T
374 " K = %f ", iter, step, Fnorm, A->nz, K);
375 }
376#endif
377
378 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
379
380#ifdef TIME
381 total_cpu += (double)(clock() - start0) / CLOCKS_PER_SEC;
382 if (Verbose) fprintf(stderr, "\n time for qtree = %f, qtree_force = %f, total cpu = %f\n",qtree_new_cpu, qtree_cpu, total_cpu);
383#endif
384
385
386 RETURN:
387 ctrl->max_qtree_level = max_qtree_level;
388
389 if (A != A0) SparseMatrix_delete(A);
390 free(force);
391}
392
395 double *x, int *flag) {
396 /* 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. */
397 /* x is a point to a 1D array, x[i*dim+j] gives the coordinate of the i-th node at dimension j. */
398 SparseMatrix A = A0;
399 int m, n;
400 int i, j, k;
401 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
402 int *ia = NULL, *ja = NULL;
403 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
404 int iter = 0;
405 const bool adaptive_cooling = ctrl->adaptive_cooling;
406 double *force;
407#ifdef TIME
408 clock_t start, end, start0, start2;
409 double total_cpu = 0;
410 start0 = clock();
411#endif
412
413 fprintf(stderr,"spring_electrical_embedding_slow");
414 if (!A || maxiter <= 0) return;
415
416 m = A->m, n = A->n;
417 if (n <= 0 || dim <= 0) return;
418 force = gv_calloc(n *dim, sizeof(double));
419
420 *flag = 0;
421 if (m != n) {
423 goto RETURN;
424 }
425 assert(A->format == FORMAT_CSR);
426 A = SparseMatrix_symmetrize(A, true);
427 ia = A->ia;
428 ja = A->ja;
429
430 if (ctrl->random_start){
431 srand(ctrl->random_seed);
432 for (i = 0; i < dim*n; i++) x[i] = drand();
433 }
434 if (K < 0){
435 ctrl->K = K = average_edge_length(A, dim, x);
436 }
437 if (p >= 0) ctrl->p = p = -1;
438 KP = pow(K, 1 - p);
439 CRK = pow(C, (2.-p)/3.)/K;
440
441 f = gv_calloc(dim, sizeof(double));
442 do {
443 for (i = 0; i < dim*n; i++) force[i] = 0;
444
445 iter++;
446 Fnorm0 = Fnorm;
447 Fnorm = 0.;
448
449#ifdef TIME
450 start2 = clock();
451#endif
452
453
454 for (i = 0; i < n; i++){
455 for (k = 0; k < dim; k++) f[k] = 0.;
456 /* repulsive force K^(1 - p)/||x_i-x_j||^(1 - p) (x_i - x_j) */
457 for (j = 0; j < n; j++){
458 if (j == i) continue;
459 dist = distance_cropped(x, dim, i, j);
460 for (k = 0; k < dim; k++){
461 f[k] += KP*(x[i*dim+k] - x[j*dim+k])/pow(dist, 1.- p);
462 }
463 }
464 for (k = 0; k < dim; k++) force[i*dim+k] += f[k];
465 }
466
467
468
469 for (i = 0; i < n; i++){
470 for (k = 0; k < dim; k++) f[k] = 0.;
471 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
472 for (j = ia[i]; j < ia[i+1]; j++){
473 if (ja[j] == i) continue;
474 dist = distance(x, dim, i, ja[j]);
475 for (k = 0; k < dim; k++){
476 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
477 }
478 }
479 for (k = 0; k < dim; k++) force[i*dim+k] += f[k];
480 }
481
482
483
484 for (i = 0; i < n; i++){
485 /* normalize force */
486 for (k = 0; k < dim; k++) f[k] = force[i*dim+k];
487
488 F = 0.;
489 for (k = 0; k < dim; k++) F += f[k]*f[k];
490 F = sqrt(F);
491 Fnorm += F;
492
493 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
494
495 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
496
497 }/* done vertex i */
498
499 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
500 } while (step > tol && iter < maxiter);
501
502#ifdef DEBUG_PRINT
503 if (Verbose) {
504 fprintf(stderr, "iter = %d, step = %f Fnorm = %f nsuper = 0 nz = %d K = %f ",iter, step, Fnorm, A->nz,K);
505 }
506#endif
507
508 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
509
510#ifdef TIME
511 total_cpu += (double)(clock() - start0) / CLOCKS_PER_SEC;
512 if (Verbose) fprintf(stderr, "time for supernode = 0, total cpu = %f\n", total_cpu);
513#endif
514
515 RETURN:
516 if (A != A0) SparseMatrix_delete(A);
517 free(f);
518 free(force);
519}
520
522 spring_electrical_control *ctrl, double *x,
523 int *flag) {
524 /* x is a point to a 1D array, x[i*dim+j] gives the coordinate of the i-th node at dimension j. */
525 SparseMatrix A = A0;
526 int m, n;
527 int i, j, k;
528 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
529 int *ia = NULL, *ja = NULL;
530 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
531 int iter = 0;
532 const bool adaptive_cooling = ctrl->adaptive_cooling;
533 bool USE_QT = false;
534 int nsuper = 0, nsupermax = 10;
535 double *center = NULL, *supernode_wgts = NULL, *distances = NULL, nsuper_avg, counts = 0, counts_avg = 0;
536#ifdef TIME
537 clock_t start, end, start0, start2;
538 double qtree_cpu = 0;
539 double total_cpu = 0;
540 start0 = clock();
541#endif
542 int max_qtree_level = ctrl->max_qtree_level;
543 oned_optimizer qtree_level_optimizer = {0};
544
545 if (!A || maxiter <= 0) return;
546
547 m = A->m, n = A->n;
548 if (n <= 0 || dim <= 0) return;
549
550 if (n >= quadtree_size) {
551 USE_QT = true;
552 qtree_level_optimizer = oned_optimizer_new(max_qtree_level);
553 center = gv_calloc(nsupermax * dim, sizeof(double));
554 supernode_wgts = gv_calloc(nsupermax, sizeof(double));
555 distances = gv_calloc(nsupermax, sizeof(double));
556 }
557 *flag = 0;
558 if (m != n) {
560 goto RETURN;
561 }
562 assert(A->format == FORMAT_CSR);
563 A = SparseMatrix_symmetrize(A, true);
564 ia = A->ia;
565 ja = A->ja;
566
567 if (ctrl->random_start){
568 srand(ctrl->random_seed);
569 for (i = 0; i < dim*n; i++) x[i] = drand();
570 }
571 if (K < 0){
572 ctrl->K = K = average_edge_length(A, dim, x);
573 }
574 if (p >= 0) ctrl->p = p = -1;
575 KP = pow(K, 1 - p);
576 CRK = pow(C, (2.-p)/3.)/K;
577
578 f = gv_calloc(dim, sizeof(double));
579 do {
580
581 iter++;
582 Fnorm0 = Fnorm;
583 Fnorm = 0.;
584 nsuper_avg = 0;
585 counts_avg = 0;
586
587 QuadTree qt = NULL;
588 if (USE_QT) {
589
590 max_qtree_level = oned_optimizer_get(qtree_level_optimizer);
591 qt = QuadTree_new_from_point_list(dim, n, max_qtree_level, x);
592
593
594 }
595#ifdef TIME
596 start2 = clock();
597#endif
598
599 for (i = 0; i < n; i++){
600 for (k = 0; k < dim; k++) f[k] = 0.;
601 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
602 for (j = ia[i]; j < ia[i+1]; j++){
603 if (ja[j] == i) continue;
604 dist = distance(x, dim, i, ja[j]);
605 for (k = 0; k < dim; k++){
606 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
607 }
608 }
609
610 /* repulsive force K^(1 - p)/||x_i-x_j||^(1 - p) (x_i - x_j) */
611 if (USE_QT){
612#ifdef TIME
613 start = clock();
614#endif
615 QuadTree_get_supernodes(qt, bh, &(x[dim*i]), i, &nsuper, &nsupermax,
616 &center, &supernode_wgts, &distances, &counts);
617
618#ifdef TIME
619 end = clock();
620 qtree_cpu += (double)(end - start) / CLOCKS_PER_SEC;
621#endif
622 counts_avg += counts;
623 nsuper_avg += nsuper;
624 for (j = 0; j < nsuper; j++){
625 dist = MAX(distances[j], MINDIST);
626 for (k = 0; k < dim; k++){
627 f[k] += supernode_wgts[j]*KP*(x[i*dim+k] - center[j*dim+k])/pow(dist, 1.- p);
628 }
629 }
630 } else {
631 for (j = 0; j < n; j++){
632 if (j == i) continue;
633 dist = distance_cropped(x, dim, i, j);
634 for (k = 0; k < dim; k++){
635 f[k] += KP*(x[i*dim+k] - x[j*dim+k])/pow(dist, 1.- p);
636 }
637 }
638 }
639
640 /* normalize force */
641 F = 0.;
642 for (k = 0; k < dim; k++) F += f[k]*f[k];
643 F = sqrt(F);
644 Fnorm += F;
645
646 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
647
648 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
649
650 }/* done vertex i */
651
652 if (qt) {
653 QuadTree_delete(qt);
654 nsuper_avg /= n;
655 counts_avg /= n;
656 oned_optimizer_train(&qtree_level_optimizer, 5 * nsuper_avg + counts_avg);
657 }
658
659 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
660 } while (step > tol && iter < maxiter);
661
662#ifdef DEBUG_PRINT
663 if (Verbose) {
664 if (USE_QT){
665 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);
666 } else {
667 fprintf(stderr, "iter = %d, step = %f Fnorm = %f nsuper = %d nz = %d K = %f ",iter, step, Fnorm, (int) nsuper_avg,A->nz,K);
668 }
669 }
670#endif
671
672 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
673
674#ifdef TIME
675 total_cpu += (double)(clock() - start0) / CLOCKS_PER_SEC;
676 if (Verbose) fprintf(stderr, "time for supernode = %f, total cpu = %f\n",qtree_cpu, total_cpu);
677#endif
678
679 RETURN:
680 if (USE_QT) {
681 ctrl->max_qtree_level = max_qtree_level;
682 }
683 if (A != A0) SparseMatrix_delete(A);
684 free(f);
685 free(center);
686 free(supernode_wgts);
687 free(distances);
688}
689
692 double *x, int *flag) {
693 /* 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
694 introduce force due to spring length
695 */
696 SparseMatrix A = A0;
697 int m, n;
698 int i, j, k;
699 double p = ctrl->p, K = ctrl->K, CRK, maxiter = ctrl->maxiter, step = ctrl->step, KP;
700 int *ia = NULL, *ja = NULL;
701 int *id = NULL, *jd = NULL;
702 double *d;
703 double *xold = NULL;
704 double *f = NULL, dist, F, Fnorm = 0, Fnorm0;
705 int iter = 0;
706 const bool adaptive_cooling = ctrl->adaptive_cooling;
707 bool USE_QT = false;
708 int nsuper = 0, nsupermax = 10;
709 double *center = NULL, *supernode_wgts = NULL, *distances = NULL, counts = 0;
710 int max_qtree_level = 10;
711
712 if (!A || maxiter <= 0) return;
713 m = A->m, n = A->n;
714 if (n <= 0 || dim <= 0) return;
715
716 if (n >= quadtree_size) {
717 USE_QT = true;
718 center = gv_calloc(nsupermax * dim, sizeof(double));
719 supernode_wgts = gv_calloc(nsupermax, sizeof(double));
720 distances = gv_calloc(nsupermax, sizeof(double));
721 }
722 *flag = 0;
723 if (m != n) {
725 goto RETURN;
726 }
727 assert(A->format == FORMAT_CSR);
728 A = SparseMatrix_symmetrize(A, true);
729 ia = A->ia;
730 ja = A->ja;
731 id = D->ia;
732 jd = D->ja;
733 d = D->a;
734
735 if (ctrl->random_start){
736 srand(ctrl->random_seed);
737 for (i = 0; i < dim*n; i++) x[i] = drand();
738 }
739 if (K < 0){
740 ctrl->K = K = average_edge_length(A, dim, x);
741 }
742 if (p >= 0) ctrl->p = p = -1;
743 KP = pow(K, 1 - p);
744 CRK = pow(C, (2.-p)/3.)/K;
745
746 f = gv_calloc(dim, sizeof(double));
747 xold = gv_calloc(dim * n, sizeof(double));
748 do {
749 iter++;
750 memcpy(xold, x, sizeof(double)*dim*n);
751 Fnorm0 = Fnorm;
752 Fnorm = 0.;
753
754 QuadTree qt = NULL;
755 if (USE_QT) {
756 qt = QuadTree_new_from_point_list(dim, n, max_qtree_level, x);
757 }
758
759 for (i = 0; i < n; i++){
760 for (k = 0; k < dim; k++) f[k] = 0.;
761 /* attractive force C^((2-p)/3) ||x_i-x_j||/K * (x_j - x_i) */
762
763 for (j = ia[i]; j < ia[i+1]; j++){
764 if (ja[j] == i) continue;
765 dist = distance(x, dim, i, ja[j]);
766 for (k = 0; k < dim; k++){
767 f[k] -= CRK*(x[i*dim+k] - x[ja[j]*dim+k])*dist;
768 }
769 }
770
771 for (j = id[i]; j < id[i+1]; j++){
772 if (jd[j] == i) continue;
773 dist = distance_cropped(x, dim, i, jd[j]);
774 for (k = 0; k < dim; k++){
775 if (dist < d[j]){
776 f[k] += 0.2*CRK*(x[i*dim+k] - x[jd[j]*dim+k])*(dist - d[j])*(dist - d[j])/dist;
777 } else {
778 f[k] -= 0.2*CRK*(x[i*dim+k] - x[jd[j]*dim+k])*(dist - d[j])*(dist - d[j])/dist;
779 }
780 /* f[k] -= 0.2*CRK*(x[i*dim+k] - x[jd[j]*dim+k])*(dist - d[j]);*/
781 }
782 }
783
784 /* repulsive force K^(1 - p)/||x_i-x_j||^(1 - p) (x_i - x_j) */
785 if (USE_QT){
786 QuadTree_get_supernodes(qt, bh, &x[dim * i], i, &nsuper, &nsupermax,
787 &center, &supernode_wgts, &distances, &counts);
788 for (j = 0; j < nsuper; j++){
789 dist = MAX(distances[j], MINDIST);
790 for (k = 0; k < dim; k++){
791 f[k] += supernode_wgts[j]*KP*(x[i*dim+k] - center[j*dim+k])/pow(dist, 1.- p);
792 }
793 }
794 } else {
795 for (j = 0; j < n; j++){
796 if (j == i) continue;
797 dist = distance_cropped(x, dim, i, j);
798 for (k = 0; k < dim; k++){
799 f[k] += KP*(x[i*dim+k] - x[j*dim+k])/pow(dist, 1.- p);
800 }
801 }
802 }
803
804 /* normalize force */
805 F = 0.;
806 for (k = 0; k < dim; k++) F += f[k]*f[k];
807 F = sqrt(F);
808 Fnorm += F;
809
810 if (F > 0) for (k = 0; k < dim; k++) f[k] /= F;
811
812 for (k = 0; k < dim; k++) x[i*dim+k] += step*f[k];
813
814 }/* done vertex i */
815
816 if (qt) QuadTree_delete(qt);
817
818 step = update_step(adaptive_cooling, step, Fnorm, Fnorm0);
819 } while (step > tol && iter < maxiter);
820
821 if (ctrl->beautify_leaves) beautify_leaves(dim, A, x);
822
823 RETURN:
824 free(xold);
825 if (A != A0) SparseMatrix_delete(A);
826 free(f);
827 free(center);
828 free(supernode_wgts);
829 free(distances);
830}
831
832static void interpolate_coord(int dim, SparseMatrix A, double *x) {
833 int i, j, k, *ia = A->ia, *ja = A->ja, nz;
834 double alpha = 0.5, beta;
835
836 double *y = gv_calloc(dim, sizeof(double));
837 for (i = 0; i < A->m; i++){
838 for (k = 0; k < dim; k++) y[k] = 0;
839 nz = 0;
840 for (j = ia[i]; j < ia[i+1]; j++){
841 if (ja[j] == i) continue;
842 nz++;
843 for (k = 0; k < dim; k++){
844 y[k] += x[ja[j]*dim + k];
845 }
846 }
847 if (nz > 0){
848 beta = (1-alpha)/nz;
849 for (k = 0; k < dim; k++) x[i*dim+k] = alpha*x[i*dim+k] + beta*y[k];
850 }
851 }
852
853 free(y);
854}
855static void prolongate(int dim, SparseMatrix A, SparseMatrix P, SparseMatrix R, double *x, double *y, double delta){
856 int nc, *ia, *ja, i, j, k;
858
860 nc = R->m;
861 ia = R->ia;
862 ja = R->ja;
863 for (i = 0; i < nc; i++){
864 for (j = ia[i]+1; j < ia[i+1]; j++){
865 for (k = 0; k < dim; k++){
866 y[ja[j]*dim + k] += delta*(drand() - 0.5);
867 }
868 }
869 }
870}
871
873 int m, max = 0, i, *ia = A->ia, *ja = A->ja, j, deg;
874 bool res = false;
875 m = A->m;
876 int *mask = gv_calloc(m + 1, sizeof(int));
877
878 for (i = 0; i < m + 1; i++){
879 mask[i] = 0;
880 }
881
882 for (i = 0; i < m; i++){
883 deg = 0;
884 for (j = ia[i]; j < ia[i+1]; j++){
885 if (i == ja[j]) continue;
886 deg++;
887 }
888 mask[deg]++;
889 max = MAX(max, mask[deg]);
890 }
891 if (mask[1] > 0.8*max && mask[1] > 0.3*m) res = true;
892 free(mask);
893 return res;
894}
895
896void pcp_rotate(int n, int dim, double *x){
897 int i, k,l;
898 double y[4], axis[2], center[2], dist, x0, x1;
899
900 assert(dim == 2);
901 for (i = 0; i < dim*dim; i++) y[i] = 0;
902 for (i = 0; i < dim; i++) center[i] = 0;
903 for (i = 0; i < n; i++){
904 for (k = 0; k < dim; k++){
905 center[k] += x[i*dim+k];
906 }
907 }
908 for (i = 0; i < dim; i++) center[i] /= n;
909 for (i = 0; i < n; i++){
910 for (k = 0; k < dim; k++){
911 x[dim*i+k] = x[dim*i+k] - center[k];
912 }
913 }
914
915 for (i = 0; i < n; i++){
916 for (k = 0; k < dim; k++){
917 for (l = 0; l < dim; l++){
918 y[dim*k+l] += x[i*dim+k]*x[i*dim+l];
919 }
920 }
921 }
922 if (y[1] == 0) {
923 axis[0] = 0; axis[1] = 1;
924 } else {
925 /* Eigensystem[{{x0, x1}, {x1, x3}}] =
926 {{(x0 + x3 - Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/2,
927 (x0 + x3 + Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/2},
928 {{-(-x0 + x3 + Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/(2*x1), 1},
929 {-(-x0 + x3 - Sqrt[x0^2 + 4*x1^2 - 2*x0*x3 + x3^2])/(2*x1), 1}}}
930 */
931 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]);
932 axis[1] = 1;
933 }
934 dist = sqrt(1+axis[0]*axis[0]);
935 axis[0] = axis[0]/dist;
936 axis[1] = axis[1]/dist;
937 for (i = 0; i < n; i++){
938 x0 = x[dim*i]*axis[0]+x[dim*i+1]*axis[1];
939 x1 = -x[dim*i]*axis[1]+x[dim*i+1]*axis[0];
940 x[dim*i] = x0;
941 x[dim*i + 1] = x1;
942
943 }
944
945
946}
947
948static void rotate(int n, int dim, double *x, double angle){
949 int i, k;
950 double axis[2], center[2], x0, x1;
951 double radian = 3.14159/180;
952
953 assert(dim == 2);
954 for (i = 0; i < dim; i++) center[i] = 0;
955 for (i = 0; i < n; i++){
956 for (k = 0; k < dim; k++){
957 center[k] += x[i*dim+k];
958 }
959 }
960 for (i = 0; i < dim; i++) center[i] /= n;
961 for (i = 0; i < n; i++){
962 for (k = 0; k < dim; k++){
963 x[dim*i+k] = x[dim*i+k] - center[k];
964 }
965 }
966 axis[0] = cos(-angle*radian);
967 axis[1] = sin(-angle*radian);
968 for (i = 0; i < n; i++){
969 x0 = x[dim*i]*axis[0]+x[dim*i+1]*axis[1];
970 x1 = -x[dim*i]*axis[1]+x[dim*i+1]*axis[0];
971 x[dim*i] = x0;
972 x[dim*i + 1] = x1;
973 }
974
975
976}
977
978static void attach_edge_label_coordinates(int dim, SparseMatrix A, int n_edge_label_nodes, int *edge_label_nodes, double *x, double *x2){
979 int i, ii, j, k;
980 int nnodes = 0;
981 double len;
982
983 int *mask = gv_calloc(A->m, sizeof(int));
984
985 for (i = 0; i < A->m; i++) mask[i] = 1;
986 for (i = 0; i < n_edge_label_nodes; i++) {
987 if (edge_label_nodes[i] >= 0 && edge_label_nodes[i] < A->m) mask[edge_label_nodes[i]] = -1;
988 }
989
990 for (i = 0; i < A->m; i++) {
991 if (mask[i] >= 0) mask[i] = nnodes++;
992 }
993
994
995 for (i = 0; i < A->m; i++){
996 if (mask[i] >= 0){
997 for (k = 0; k < dim; k++) x[i*dim+k] = x2[mask[i]*dim+k];
998 }
999 }
1000
1001 for (i = 0; i < n_edge_label_nodes; i++){
1002 ii = edge_label_nodes[i];
1003 len = A->ia[ii+1] - A->ia[ii];
1004 assert(len >= 2); /* should just be 2 */
1005 assert(mask[ii] < 0);
1006 for (k = 0; k < dim; k++) {
1007 x[ii*dim+k] = 0;
1008 }
1009 for (j = A->ia[ii]; j < A->ia[ii+1]; j++){
1010 for (k = 0; k < dim; k++){
1011 x[ii * dim + k] += x[A->ja[j] * dim + k];
1012 }
1013 }
1014 for (k = 0; k < dim; k++) {
1015 x[ii*dim+k] /= len;
1016 }
1017 }
1018
1019 free(mask);
1020}
1021
1022static SparseMatrix shorting_edge_label_nodes(SparseMatrix A, int n_edge_label_nodes, int *edge_label_nodes){
1023 int id = 0;
1024 int *ia = A->ia, *ja = A->ja;
1025
1026 int *mask = gv_calloc(A->m, sizeof(int));
1027
1028 for (int i = 0; i < A->m; i++) mask[i] = 1;
1029
1030 for (int i = 0; i < n_edge_label_nodes; i++){
1031 mask[edge_label_nodes[i]] = -1;
1032 }
1033
1034 for (int i = 0; i < A->m; i++) {
1035 if (mask[i] > 0) mask[i] = id++;
1036 }
1037
1038 LIST(int) irn = {0};
1039 LIST(int) jcn = {0};
1040 for (int i = 0; i < A->m; i++){
1041 if (mask[i] < 0) continue;
1042 for (int j = ia[i]; j < ia[i+1]; j++){
1043 if (mask[ja[j]] >= 0) {
1044 LIST_APPEND(&irn, mask[i]);
1045 LIST_APPEND(&jcn, mask[ja[j]]);
1046 continue;
1047 }
1048 const int ii = ja[j];
1049 for (int jj = ia[ii]; jj < ia[ii+1]; jj++){
1050 if (ja[jj] != i && mask[ja[jj]] >= 0) {
1051 LIST_APPEND(&irn, mask[i]);
1052 LIST_APPEND(&jcn, mask[ja[jj]]);
1053 }
1054 }
1055 }
1056 }
1057
1058 LIST_SYNC(&irn);
1059 LIST_SYNC(&jcn);
1061 LIST_FRONT(&irn),
1062 LIST_FRONT(&jcn), NULL,
1064 sizeof(double));
1065
1066 LIST_FREE(&irn);
1067 LIST_FREE(&jcn);
1068 free(mask);
1069 return B;
1070
1071}
1072
1075 double *label_sizes, double *x,
1076 int n_edge_label_nodes,
1077 int *edge_label_nodes, int *flag) {
1078
1079 int n;
1080 SparseMatrix A = A0, P = NULL;
1081 Multilevel grid, grid0;
1082 double *xc = NULL, *xf = NULL;
1083#ifdef TIME
1084 clock_t cpu;
1085#endif
1086
1087 const spring_electrical_control ctrl0 = *ctrl;
1088
1089#ifdef TIME
1090 cpu = clock();
1091#endif
1092
1093 *flag = 0;
1094 if (!A) return;
1095 n = A->n;
1096 if (n <= 0 || dim <= 0) return;
1097
1098 if (!SparseMatrix_is_symmetric(A, false) || A->type != MATRIX_TYPE_REAL){
1100 } else {
1102 }
1103
1104 /* we first generate a layout discarding (shorting) the edge labels nodes, then assign the edge label nodes at the average of their neighbors */
1106 && n_edge_label_nodes > 0){
1107 SparseMatrix A2;
1108
1109 double *x2 = gv_calloc(A->m * dim, sizeof(double));
1110 A2 = shorting_edge_label_nodes(A, n_edge_label_nodes, edge_label_nodes);
1111 multilevel_spring_electrical_embedding(dim, A2, ctrl, NULL, x2, 0, NULL, flag);
1112
1113 assert(!*flag);
1114 attach_edge_label_coordinates(dim, A, n_edge_label_nodes, edge_label_nodes, x, x2);
1115 remove_overlap(dim, A, x, label_sizes, ctrl->overlap, ctrl->initial_scaling,
1116 ctrl->edge_labeling_scheme, n_edge_label_nodes, edge_label_nodes, A, ctrl->do_shrinking);
1118 free(x2);
1119 if (A != A0) SparseMatrix_delete(A);
1120
1121 return;
1122 }
1123
1124 Multilevel_control mctrl = {.maxlevel = ctrl->multilevels};
1125 grid0 = Multilevel_new(A, mctrl);
1126
1129 xc = x;
1130 } else {
1131 xc = gv_calloc(grid->n * dim, sizeof(double));
1132 }
1133
1134 const bool plg = power_law_graph(A);
1135 if (ctrl->p == AUTOP){
1136 ctrl->p = -1;
1137 if (plg) ctrl->p = -1.8;
1138 }
1139
1140 do {
1141#ifdef DEBUG_PRINT
1142 if (Verbose) {
1143 print_padding(grid->level);
1145 fprintf(stderr, "coarsest level -- %d, n = %d\n", grid->level, grid->n);
1146 } else {
1147 fprintf(stderr, "level -- %d, n = %d\n", grid->level, grid->n);
1148 }
1149 }
1150#endif
1151 if (ctrl->tscheme == QUAD_TREE_NONE){
1152 spring_electrical_embedding_slow(dim, grid->A, ctrl, xc, flag);
1153 } else if (ctrl->tscheme == QUAD_TREE_FAST || (ctrl->tscheme == QUAD_TREE_HYBRID && grid->A->m > QUAD_TREE_HYBRID_SIZE)){
1154 if (ctrl->tscheme == QUAD_TREE_HYBRID && grid->A->m > 10 && Verbose){
1155 fprintf(stderr, "QUAD_TREE_HYBRID, size larger than %d, switch to fast quadtree", QUAD_TREE_HYBRID_SIZE);
1156 }
1157 spring_electrical_embedding_fast(dim, grid->A, ctrl, xc, flag);
1158 } else {
1159 spring_electrical_embedding(dim, grid->A, ctrl, xc, flag);
1160 }
1161 if (Multilevel_is_finest(grid)) break;
1162 if (*flag) {
1163 free(xc);
1164 goto RETURN;
1165 }
1166 P = grid->P;
1167 grid = grid->prev;
1169 xf = x;
1170 } else {
1171 xf = gv_calloc(grid->n * dim, sizeof(double));
1172 }
1173 prolongate(dim, grid->A, P, grid->R, xc, xf, ctrl->K * 0.001);
1174 free(xc);
1175 xc = xf;
1176 ctrl->random_start = false;
1177 ctrl->K = ctrl->K * 0.75;
1178 ctrl->adaptive_cooling = false;
1179 ctrl->step = .1;
1180 } while (grid);
1181
1182#ifdef TIME
1183 if (Verbose)
1184 fprintf(stderr, "layout time %f\n", (double)(clock() - cpu) / CLOCKS_PER_SEC);
1185 cpu = clock();
1186#endif
1187
1188 post_process_smoothing(dim, A, *ctrl, x);
1189
1190 if (Verbose) fprintf(stderr, "ctrl->overlap=%d\n",ctrl->overlap);
1191
1192 /* rotation has to be done before overlap removal, since rotation could induce overlaps */
1193 if (dim == 2){
1194 pcp_rotate(n, dim, x);
1195 }
1196 if (ctrl->rotation != 0) rotate(n, dim, x, ctrl->rotation);
1197
1198
1199 remove_overlap(dim, A, x, label_sizes, ctrl->overlap, ctrl->initial_scaling,
1200 ctrl->edge_labeling_scheme, n_edge_label_nodes, edge_label_nodes, A, ctrl->do_shrinking);
1201
1202 RETURN:
1203 *ctrl = ctrl0;
1204 if (A != A0) SparseMatrix_delete(A);
1205 Multilevel_delete(grid0);
1206 }
void print_padding(int n)
Definition Multilevel.c:241
Multilevel Multilevel_get_coarsest(Multilevel grid)
Definition Multilevel.c:296
void Multilevel_delete(Multilevel grid)
Definition Multilevel.c:37
Multilevel Multilevel_new(SparseMatrix A0, const Multilevel_control ctrl)
Definition Multilevel.c:280
#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:281
QuadTree QuadTree_new_from_point_list(int dim, int n, int max_level, double *coord)
Definition QuadTree.c:309
void QuadTree_delete(QuadTree q)
Definition QuadTree.c:375
void QuadTree_get_supernodes(QuadTree qt, double bh, double *pt, int nodeid, int *nsuper, int *nsupermax, double **center, double **supernode_wgts, double **distances, double *counts)
Definition QuadTree.c:93
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, int 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:16
#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:22
double distance(double *x, int dim, int i, int j)
Definition general.c:121
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 *)
node NULL
Definition grammar.y:181
@ grid
Definition gvgen.c:34
#define B
Definition hierarchy.c:118
#define D
Definition hierarchy.c:120
type-generic dynamically expanding list
#define LIST(type)
Definition list.h:55
#define LIST_SIZE(list)
Definition list.h:80
#define LIST_APPEND(list, item)
Definition list.h:120
#define LIST_FREE(list)
Definition list.h:370
#define LIST_FRONT(list)
Definition list.h:180
#define LIST_IS_EMPTY(list)
Definition list.h:90
#define LIST_SYNC(list)
Definition list.h:327
#define LIST_GET(list, index)
Definition list.h:155
#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:586
@ ELSCHEME_STRAIGHTLINE_PENALTY2
Definition overlap.h:30
@ ELSCHEME_STRAIGHTLINE_PENALTY
Definition overlap.h:30
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:153
#define alpha
Definition shapes.c:4056
static bool power_law_graph(SparseMatrix A)
void spring_electrical_embedding(int dim, SparseMatrix A0, spring_electrical_control *ctrl, double *x, int *flag)
void spring_electrical_embedding_fast(int dim, SparseMatrix A0, spring_electrical_control *ctrl, double *x, int *flag)
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)
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 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
@ OPT_INIT
@ OPT_DOWN
double average_edge_length(SparseMatrix A, int dim, double *coord)
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)
void spring_electrical_spring_embedding(int dim, SparseMatrix A0, SparseMatrix D, spring_electrical_control *ctrl, double *x, int *flag)
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)
@ ERROR_NOT_SQUARE_MATRIX
#define AUTOP
@ QUAD_TREE_FAST
@ QUAD_TREE_HYBRID
@ QUAD_TREE_NONE
@ SMOOTHING_NONE
@ QUAD_TREE_HYBRID_SIZE
#define RETURN(v)
Definition strmatch.c:144
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