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