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