Graphviz 12.1.1~dev.20240814.0213
Loading...
Searching...
No Matches
rank.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
12/*
13 * Rank the nodes of a directed graph, subject to user-defined
14 * sets of nodes to be kept on the same, min, or max rank.
15 * The temporary acyclic fast graph is constructed and ranked
16 * by a network-simplex technique. Then ranks are propagated
17 * to non-leader nodes and temporary edges are deleted.
18 * Leaf nodes and top-level clusters are left collapsed, though.
19 * Assigns global minrank and maxrank of graph and all clusters.
20 *
21 * TODO: safety code. must not be in two clusters at same level.
22 * must not be in same/min/max/rank and a cluster at the same time.
23 * watch out for interactions between leaves and clusters.
24 */
25
26#include <cgraph/alloc.h>
27#include <cgraph/gv_math.h>
28#include <dotgen/dot.h>
29#include <limits.h>
30#include <stdbool.h>
31#include <stddef.h>
32#include <stdint.h>
33
34static void dot1_rank(graph_t *g);
35static void dot2_rank(graph_t *g);
36
37static void
39{
40 for (size_t i = L->size; i != SIZE_MAX; i--)
41 L->list[i] = NULL;
42 L->size = 0;
43}
44
45static void
47{
48 node_t *n;
49 edge_t *e, *f;
50
51 for (size_t c = 0; c < GD_comp(g).size; c++) {
52 GD_nlist(g) = GD_comp(g).list[c];
53 for (n = GD_nlist(g); n; n = ND_next(n)) {
54 renewlist(&ND_in(n));
55 renewlist(&ND_out(n));
56 ND_mark(n) = false;
57 }
58 }
59 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
60 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
61 f = ED_to_virt(e);
62 /* Null out any other references to f to make sure we don't
63 * handle it a second time. For example, parallel multiedges
64 * share a virtual edge.
65 */
66 if (f && (e != ED_to_orig(f))) {
67 ED_to_virt(e) = NULL;
68 }
69 }
70 }
71 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
72 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
73 f = ED_to_virt(e);
74 if (f && ED_to_orig(f) == e) {
75 free(f->base.data);
76 free(f);
77 ED_to_virt(e) = NULL;
78 }
79 }
80 }
81 free(GD_comp(g).list);
82 GD_comp(g).list = NULL;
83 GD_comp(g).size = 0;
84}
85
86/* When there are edge labels, extra ranks are reserved here for the virtual
87 * nodes of the labels. This is done by doubling the input edge lengths.
88 * The input rank separation is adjusted to compensate.
89 */
90static void
92{
93 node_t *n;
94 edge_t *e;
95
96 if (GD_has_labels(g) & EDGE_LABEL) {
97 for (n = agfstnode(g); n; n = agnxtnode(g, n))
98 for (e = agfstout(g, n); e; e = agnxtout(g, e))
99 ED_minlen(e) *= 2;
100 GD_ranksep(g) = (GD_ranksep(g) + 1) / 2;
101 }
102}
103
104/* Merge the nodes of a min, max, or same rank set. */
105static void
106collapse_rankset(graph_t * g, graph_t * subg, int kind)
107{
108 node_t *u, *v;
109
110 u = v = agfstnode(subg);
111 if (u) {
112 ND_ranktype(u) = kind;
113 while ((v = agnxtnode(subg, v))) {
114 UF_union(u, v);
115 ND_ranktype(v) = ND_ranktype(u);
116 }
117 switch (kind) {
118 case MINRANK:
119 case SOURCERANK:
120 if (GD_minset(g) == NULL)
121 GD_minset(g) = u;
122 else
123 GD_minset(g) = UF_union(GD_minset(g), u);
124 break;
125 case MAXRANK:
126 case SINKRANK:
127 if (GD_maxset(g) == NULL)
128 GD_maxset(g) = u;
129 else
130 GD_maxset(g) = UF_union(GD_maxset(g), u);
131 break;
132 }
133 switch (kind) {
134 case SOURCERANK:
135 ND_ranktype(GD_minset(g)) = kind;
136 break;
137 case SINKRANK:
138 ND_ranktype(GD_maxset(g)) = kind;
139 break;
140 }
141 }
142}
143
144static int
146{
147 static char *name[] = { "same", "min", "source", "max", "sink", NULL };
148 static int class[] =
150 int val;
151
152 if (is_cluster(g))
153 return CLUSTER;
154 val = maptoken(agget(g, "rank"), name, class);
155 GD_set_type(g) = val;
156 return val;
157}
158
159static int
161{
162 int cno;
163 cno = ++(GD_n_cluster(g));
164 GD_clust(g) = gv_recalloc(GD_clust(g), GD_n_cluster(g), cno + 1,
165 sizeof(graph_t*));
166 GD_clust(g)[cno] = subg;
167 do_graph_label(subg);
168 return cno;
169}
170
171static void
173{
174 node_t *n, *nn;
175 edge_t *e;
176 int i;
177
178 /* enforce that a node is in at most one cluster at this level */
179 for (n = agfstnode(g); n; n = nn) {
180 nn = agnxtnode(g, n);
181 if (ND_ranktype(n)) {
182 agdelete(g, n);
183 continue;
184 }
185 for (i = 1; i < GD_n_cluster(par); i++)
186 if (agcontains(GD_clust(par)[i], n))
187 break;
188 if (i < GD_n_cluster(par))
189 agdelete(g, n);
190 ND_clust(n) = NULL;
191 }
192
193 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
194 for (e = agfstout(dot_root(g), n); e; e = agnxtout(dot_root(g), e)) {
195 if (agcontains(g, aghead(e)))
196 agsubedge(g,e,1);
197 }
198 }
199}
200
201void
203{
204 node_t *n, *leader = NULL;
205 GD_minrank(g) = INT_MAX;
206 GD_maxrank(g) = -1;
207 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
208 if (GD_maxrank(g) < ND_rank(n))
209 GD_maxrank(g) = ND_rank(n);
210 if (GD_minrank(g) > ND_rank(n))
211 GD_minrank(g) = ND_rank(n);
212 if (leader == NULL)
213 leader = n;
214 else {
215 if (ND_rank(n) < ND_rank(leader))
216 leader = n;
217 }
218 }
219 GD_leader(g) = leader;
220}
221
222static void
224{
225 node_t *leader, *n;
226 int maxrank = 0;
227
228 /* find number of ranks and select a leader */
229 leader = NULL;
230 for (n = GD_nlist(clust); n; n = ND_next(n)) {
231 if (ND_rank(n) == 0 && ND_node_type(n) == NORMAL)
232 leader = n;
233 if (maxrank < ND_rank(n))
234 maxrank = ND_rank(n);
235 }
236 assert(leader != NULL);
237 GD_leader(clust) = leader;
238
239 for (n = agfstnode(clust); n; n = agnxtnode(clust, n)) {
240 assert(ND_UF_size(n) <= 1 || n == leader);
241 UF_union(n, leader);
242 ND_ranktype(n) = CLUSTER;
243 }
244}
245
246/*
247 * A cluster is collapsed in three steps.
248 * 1) The nodes of the cluster are ranked locally.
249 * 2) The cluster is collapsed into one node on the least rank.
250 * 3) In class1(), any inter-cluster edges are converted using
251 * the "virtual node + 2 edges" trick.
252 */
253static void
255{
256 if (GD_parent(subg)) {
257 return;
258 }
259 GD_parent(subg) = g;
260 node_induce(g, subg);
261 if (agfstnode(subg) == NULL)
262 return;
263 make_new_cluster(g, subg);
264 if (CL_type == LOCAL) {
265 dot1_rank(subg);
266 cluster_leader(subg);
267 } else
268 dot_scan_ranks(subg);
269}
270
271/* Execute union commands for "same rank" subgraphs and clusters. */
272static void
274{
275 int c;
276 graph_t *subg;
277
278 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
279 c = rank_set_class(subg);
280 if (c) {
281 if ((c == CLUSTER) && CL_type == LOCAL)
282 collapse_cluster(rg, subg);
283 else
284 collapse_rankset(rg, subg, c);
285 }
286 else collapse_sets(rg, subg);
287
288 }
289}
290
291static void
293{
294 graph_t *subg;
295 for (subg = agfstsubg(dot_root(g)); subg; subg = agnxtsubg(subg)) {
296 if (GD_set_type(subg) == CLUSTER)
297 collapse_cluster(g, subg);
298 }
299}
300
301static void
303{
304 int c;
305
306 GD_minrank(g) += ND_rank(GD_leader(g));
307 GD_maxrank(g) += ND_rank(GD_leader(g));
308 for (c = 1; c <= GD_n_cluster(g); c++)
309 set_minmax(GD_clust(g)[c]);
310}
311
312/* To ensure that min and max rank nodes always have the intended rank
313 * assignment, reverse any incompatible edges.
314 */
315static point
317{
318 node_t *n;
319 edge_t *e;
320 point slen;
321
322 slen.x = slen.y = 0;
323 if ((GD_maxset(g) == NULL) && (GD_minset(g) == NULL))
324 return slen;
325 if (GD_minset(g) != NULL)
326 GD_minset(g) = UF_find(GD_minset(g));
327 if (GD_maxset(g) != NULL)
328 GD_maxset(g) = UF_find(GD_maxset(g));
329
330 if ((n = GD_maxset(g))) {
331 slen.y = (ND_ranktype(GD_maxset(g)) == SINKRANK);
332 while ((e = ND_out(n).list[0])) {
333 assert(aghead(e) == UF_find(aghead(e)));
334 reverse_edge(e);
335 }
336 }
337 if ((n = GD_minset(g))) {
338 slen.x = (ND_ranktype(GD_minset(g)) == SOURCERANK);
339 while ((e = ND_in(n).list[0])) {
340 assert(agtail(e) == UF_find(agtail(e)));
341 reverse_edge(e);
342 }
343 }
344 return slen;
345}
346
347static int
349{
350 node_t *n;
351 edge_t *e = 0;
352
353 if ((GD_maxset(g)) || (GD_minset(g))) {
354 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
355 if (n != UF_find(n))
356 continue;
357 if ((ND_out(n).size == 0) && GD_maxset(g) && (n != GD_maxset(g))) {
358 e = virtual_edge(n, GD_maxset(g), NULL);
359 ED_minlen(e) = slen.y;
360 ED_weight(e) = 0;
361 }
362 if ((ND_in(n).size == 0) && GD_minset(g) && (n != GD_minset(g))) {
363 e = virtual_edge(GD_minset(g), n, NULL);
364 ED_minlen(e) = slen.x;
365 ED_weight(e) = 0;
366 }
367 }
368 }
369 return (e != 0);
370}
371
372/* Run the network simplex algorithm on each component. */
373void rank1(graph_t * g)
374{
375 int maxiter = INT_MAX;
376 char *s;
377
378 if ((s = agget(g, "nslimit1")))
379 maxiter = scale_clamp(agnnodes(g), atof(s));
380 for (size_t c = 0; c < GD_comp(g).size; c++) {
381 GD_nlist(g) = GD_comp(g).list[c];
382 rank(g, (GD_n_cluster(g) == 0 ? 1 : 0), maxiter); /* TB balance */
383 }
384}
385
386/*
387 * Assigns ranks of non-leader nodes.
388 * Expands same, min, max rank sets.
389 * Leaf sets and clusters remain merged.
390 * Sets minrank and maxrank appropriately.
391 */
392static void expand_ranksets(graph_t *g) {
393 int c;
394 node_t *n, *leader;
395
396 if ((n = agfstnode(g))) {
397 GD_minrank(g) = INT_MAX;
398 GD_maxrank(g) = -1;
399 while (n) {
400 leader = UF_find(n);
401 /* The following works because ND_rank(n) == 0 if n is not in a
402 * cluster, and ND_rank(n) = the local rank offset if n is in
403 * a cluster. */
404 if (leader != n)
405 ND_rank(n) += ND_rank(leader);
406
407 if (GD_maxrank(g) < ND_rank(n))
408 GD_maxrank(g) = ND_rank(n);
409 if (GD_minrank(g) > ND_rank(n))
410 GD_minrank(g) = ND_rank(n);
411
412 if (ND_ranktype(n) && (ND_ranktype(n) != LEAFSET))
413 UF_singleton(n);
414 n = agnxtnode(g, n);
415 }
416 if (g == dot_root(g)) {
417 if (CL_type == LOCAL) {
418 for (c = 1; c <= GD_n_cluster(g); c++)
419 set_minmax(GD_clust(g)[c]);
420 } else {
421 find_clusters(g);
422 }
423 }
424 } else {
425 GD_minrank(g) = GD_maxrank(g) = 0;
426 }
427}
428
429static void dot1_rank(graph_t *g)
430{
431 point p;
433
434 collapse_sets(g,g);
435 /*collapse_leaves(g); */
436 class1(g);
437 p = minmax_edges(g);
438 decompose(g, 0);
439 acyclic(g);
440 if (minmax_edges2(g, p))
441 decompose(g, 0);
442
443 rank1(g);
444
446 cleanup1(g);
447}
448
450 if (mapbool(agget(g, "newrank"))) {
451 GD_flags(g) |= NEW_RANK;
452 dot2_rank(g);
453 }
454 else
455 dot1_rank(g);
456 if (Verbose)
457 fprintf (stderr, "Maxrank = %d, minrank = %d\n", GD_maxrank(g), GD_minrank(g));
458}
459
461{
462 return is_a_cluster(g); // from utils.c
463}
464
465/* new ranking code:
466 * Allows more constraints
467 * Copy of level.c in dotgen2
468 * Many of the utility functions are simpler or gone with
469 * cgraph library.
470 */
471#define BACKWARD_PENALTY 1000
472#define STRONG_CLUSTER_WEIGHT 1000
473#define NORANK 6
474#define ROOT "\177root"
475#define TOPNODE "\177top"
476#define BOTNODE "\177bot"
477
478/* hops is not used in dot, so we overload it to
479 * contain the index of the connected component
480 */
481#define ND_comp(n) ND_hops(n)
482
483static void set_parent(graph_t* g, graph_t* p)
484{
485 GD_parent(g) = p;
486 make_new_cluster(p, g);
487 node_induce(p, g);
488}
489
490static bool is_empty(graph_t *g) {
491 return !agfstnode(g);
492}
493
495{
496 char *str = agget(g, "compact");
497 return mapbool(str);
498}
499
500static int rankset_kind(graph_t * g)
501{
502 char *str = agget(g, "rank");
503
504 if (str && str[0]) {
505 if (!strcmp(str, "min"))
506 return MINRANK;
507 if (!strcmp(str, "source"))
508 return SOURCERANK;
509 if (!strcmp(str, "max"))
510 return MAXRANK;
511 if (!strcmp(str, "sink"))
512 return SINKRANK;
513 if (!strcmp(str, "same"))
514 return SAMERANK;
515 }
516 return NORANK;
517}
518
519static bool is_nonconstraint(edge_t * e)
520{
521 char *constr;
522
523 if (E_constr && (constr = agxget(e, E_constr))) {
524 if (constr[0] && !mapbool(constr))
525 return true;
526 }
527 return false;
528}
529
530static node_t *find(node_t * n)
531{
532 node_t *set;
533 if ((set = ND_set(n))) {
534 if (set != n)
535 set = ND_set(n) = find(set);
536 } else
537 set = ND_set(n) = n;
538 return set;
539}
540
541static node_t *union_one(node_t * leader, node_t * n)
542{
543 if (n)
544 return (ND_set(find(n)) = find(leader));
545 else
546 return leader;
547}
548
550{
551 node_t *n, *leader;
552
553 n = agfstnode(g);
554 if (!n)
555 return n;
556 leader = find(n);
557 while ((n = agnxtnode(g, n)))
558 union_one(leader, n);
559 return leader;
560}
561
562static void compile_samerank(graph_t * ug, graph_t * parent_clust)
563{
564 graph_t *s; /* subgraph being scanned */
565 graph_t *clust; /* cluster that contains the rankset */
566 node_t *n, *leader;
567
568 if (is_empty(ug))
569 return;
570 if (is_a_cluster(ug)) {
571 clust = ug;
572 if (parent_clust) {
573 GD_level(ug) = GD_level(parent_clust) + 1;
574 set_parent(ug, parent_clust);
575 }
576 else
577 GD_level(ug) = 0;
578 } else
579 clust = parent_clust;
580
581 /* process subgraphs of this subgraph */
582 for (s = agfstsubg(ug); s; s = agnxtsubg(s))
583 compile_samerank(s, clust);
584
585 /* process this subgraph as a cluster */
586 if (is_a_cluster(ug)) {
587 for (n = agfstnode(ug); n; n = agnxtnode(ug, n)) {
588 if (ND_clust(n) == 0)
589 ND_clust(n) = ug;
590#ifdef DEBUG
591 fprintf(stderr, "(%s) %s %p\n", agnameof(ug), agnameof(n),
592 ND_clust(n));
593#endif
594 }
595 }
596
597 /* process this subgraph as a rankset */
598 switch (rankset_kind(ug)) {
599 case SOURCERANK: // fall through
600 case MINRANK:
601 leader = union_all(ug);
602 if (clust != NULL) {
603 GD_minrep(clust) = union_one(leader, GD_minrep(clust));
604 }
605 break;
606 case SINKRANK: // fall through
607 case MAXRANK:
608 leader = union_all(ug);
609 if (clust != NULL) {
610 GD_maxrep(clust) = union_one(leader, GD_maxrep(clust));
611 }
612 break;
613 case SAMERANK:
614 leader = union_all(ug);
615 /* do we need to record these ranksets? */
616 break;
617 case NORANK:
618 break;
619 default: /* unrecognized - warn and do nothing */
620 agwarningf("%s has unrecognized rank=%s", agnameof(ug),
621 agget(ug, "rank"));
622 }
623
624 /* a cluster may become degenerate */
625 if (is_a_cluster(ug) && GD_minrep(ug)) {
626 if (GD_minrep(ug) == GD_maxrep(ug)) {
627 node_t *up = union_all(ug);
628 GD_minrep(ug) = up;
629 GD_maxrep(ug) = up;
630 }
631 }
632}
633
634static graph_t *dot_lca(graph_t * c0, graph_t * c1)
635{
636 while (c0 != c1) {
637 if (GD_level(c0) >= GD_level(c1))
638 c0 = GD_parent(c0);
639 else
640 c1 = GD_parent(c1);
641 }
642 return c0;
643}
644
646{
647 graph_t *par, *ct, *ch;
648 ct = ND_clust(agtail(e));
649 ch = ND_clust(aghead(e));
650 if (ct == ch)
651 return true;
652 par = dot_lca(ct, ch);
653 if (par == ct || par == ch)
654 return true;
655 return false;
656}
657
659static node_t* makeXnode (graph_t* G, char* name)
660{
661 node_t *n = agnode(G, name, 1);
662 alloc_elist(4, ND_in(n));
663 alloc_elist(4, ND_out(n));
664 if (Last_node) {
665 ND_prev(n) = Last_node;
666 ND_next(Last_node) = n;
667 } else {
668 ND_prev(n) = NULL;
669 GD_nlist(G) = n;
670 }
671 Last_node = n;
672 ND_next(n) = NULL;
673
674 return n;
675}
676
677static void compile_nodes(graph_t * g, graph_t * Xg)
678{
679 /* build variables */
680 node_t *n;
681
682 Last_node = NULL;
683 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
684 if (find(n) == n)
685 ND_rep(n) = makeXnode (Xg, agnameof(n));
686 }
687 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
688 if (ND_rep(n) == 0)
689 ND_rep(n) = ND_rep(find(n));
690 }
691}
692
693static void merge(edge_t * e, int minlen, int weight)
694{
695 ED_minlen(e) = MAX(ED_minlen(e), minlen);
696 ED_weight(e) += weight;
697}
698
699static void strong(graph_t * g, node_t * t, node_t * h, edge_t * orig)
700{
701 edge_t *e;
702 if ((e = agfindedge(g, t, h)) ||
703 (e = agfindedge(g, h, t)) || (e = agedge(g, t, h, 0, 1)))
704 merge(e, ED_minlen(orig), ED_weight(orig));
705 else
706 agerrorf("ranking: failure to create strong constraint edge between nodes %s and %s\n",
707 agnameof(t), agnameof(h));
708}
709
710static void weak(graph_t * g, node_t * t, node_t * h, edge_t * orig)
711{
712 node_t *v;
713 edge_t *e, *f;
714 static int id;
715 char buf[100];
716
717 for (e = agfstin(g, t); e; e = agnxtin(g, e)) {
718 /* merge with existing weak edge (e,f) */
719 v = agtail(e);
720 if ((f = agfstout(g, v)) && (aghead(f) == h)) {
721 return;
722 }
723 }
724 if (!e) {
725 snprintf(buf, sizeof(buf), "_weak_%d", id++);
726 v = makeXnode(g, buf);
727 e = agedge(g, v, t, 0, 1);
728 f = agedge(g, v, h, 0, 1);
729 }
730 ED_minlen(e) = MAX(ED_minlen(e), 0); /* effectively a nop */
732 ED_minlen(f) = MAX(ED_minlen(f), ED_minlen(orig));
733 ED_weight(f) += ED_weight(orig);
734}
735
736static void compile_edges(graph_t * ug, graph_t * Xg)
737{
738 node_t *n;
739 edge_t *e;
740 node_t *Xt, *Xh;
741 graph_t *tc, *hc;
742
743 /* build edge constraints */
744 for (n = agfstnode(ug); n; n = agnxtnode(ug, n)) {
745 Xt = ND_rep(n);
746 for (e = agfstout(ug, n); e; e = agnxtout(ug, e)) {
747 if (is_nonconstraint(e))
748 continue;
749 Xh = ND_rep(find(aghead(e)));
750 if (Xt == Xh)
751 continue;
752
753 tc = ND_clust(agtail(e));
754 hc = ND_clust(aghead(e));
755
756 if (is_internal_to_cluster(e)) {
757 graph_t *clust_tail = ND_clust(agtail(e));
758 graph_t *clust_head = ND_clust(aghead(e));
759 /* determine if graph requires reversed edge */
760 if ((clust_tail != NULL && find(agtail(e)) == GD_maxrep(clust_tail))
761 || (clust_head != NULL && find(aghead(e)) == GD_minrep(clust_head))) {
762 node_t *temp = Xt;
763 Xt = Xh;
764 Xh = temp;
765 }
766 strong(Xg, Xt, Xh, e);
767 } else {
769 weak(Xg, Xt, Xh, e);
770 else
771 strong(Xg, Xt, Xh, e);
772 }
773 }
774 }
775}
776
777static void compile_clusters(graph_t* g, graph_t* Xg, node_t* top, node_t* bot)
778{
779 node_t *n;
780 node_t *rep;
781 edge_t *e;
782 graph_t *sub;
783
784 if (is_a_cluster(g) && is_a_strong_cluster(g)) {
785 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
786 if (agfstin(g, n) == 0) {
787 rep = ND_rep(find(n));
788 if (!top) top = makeXnode(Xg,TOPNODE);
789 agedge(Xg, top, rep, 0, 1);
790 }
791 if (agfstout(g, n) == 0) {
792 rep = ND_rep(find(n));
793 if (!bot) bot = makeXnode(Xg,BOTNODE);
794 agedge(Xg, rep, bot, 0, 1);
795 }
796 }
797 if (top && bot) {
798 e = agedge(Xg, top, bot, 0, 1);
800 }
801 }
802 for (sub = agfstsubg(g); sub; sub = agnxtsubg(sub))
803 compile_clusters(sub, Xg, top, bot);
804}
805
806static void reverse_edge2(graph_t * g, edge_t * e)
807{
808 edge_t *rev;
809
810 rev = agfindedge(g, aghead(e), agtail(e));
811 if (!rev)
812 rev = agedge(g, aghead(e), agtail(e), 0, 1);
813 merge(rev, ED_minlen(e), ED_weight(e));
814 agdelete(g, e);
815}
816
817static void dfs(graph_t * g, node_t * v)
818{
819 edge_t *e, *f;
820 node_t *w;
821
822 if (ND_mark(v))
823 return;
824 ND_mark(v) = true;
825 ND_onstack(v) = true;
826 for (e = agfstout(g, v); e; e = f) {
827 f = agnxtout(g, e);
828 w = aghead(e);
829 if (ND_onstack(w))
830 reverse_edge2(g, e);
831 else {
832 if (!ND_mark(w))
833 dfs(g, w);
834 }
835 }
836 ND_onstack(v) = false;
837}
838
839static void break_cycles(graph_t * g)
840{
841 node_t *n;
842
843 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
844 ND_mark(n) = false;
845 ND_onstack(n) = false;
846 }
847 for (n = agfstnode(g); n; n = agnxtnode(g, n))
848 dfs(g, n);
849}
850/* setMinMax:
851 * This will only be called with the root graph or a cluster
852 * which are guaranteed to contain nodes. Thus, leader will be
853 * set.
854 */
855static void setMinMax (graph_t* g, int doRoot)
856{
857 int c, v;
858 node_t *n;
859 node_t* leader = NULL;
860
861 /* Do child clusters */
862 for (c = 1; c <= GD_n_cluster(g); c++)
863 setMinMax(GD_clust(g)[c], 0);
864
865 if (!GD_parent(g) && !doRoot) // root graph
866 return;
867
868 GD_minrank(g) = INT_MAX;
869 GD_maxrank(g) = -1;
870 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
871 v = ND_rank(n);
872 if (GD_maxrank(g) < v)
873 GD_maxrank(g) = v;
874 if (GD_minrank(g) > v) {
875 GD_minrank(g) = v;
876 leader = n;
877 }
878 }
879 GD_leader(g) = leader;
880}
881
882/* readout_levels:
883 * Store node rank information in original graph.
884 * Set rank bounds in graph and clusters
885 * Free added data structures.
886 *
887 * rank2 is called with balance=1, which ensures that minrank=0
888 */
889static void readout_levels(graph_t * g, graph_t * Xg, int ncc)
890{
891 node_t *n;
892 node_t *xn;
893 int* minrk = NULL;
894 int doRoot = 0;
895
896 GD_minrank(g) = INT_MAX;
897 GD_maxrank(g) = -1;
898 if (ncc > 1) {
899 int i;
900 minrk = gv_calloc(ncc + 1, sizeof(int));
901 for (i = 1; i <= ncc; i++)
902 minrk[i] = INT_MAX;
903 }
904 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
905 xn = ND_rep(find(n));
906 ND_rank(n) = ND_rank(xn);
907 if (GD_maxrank(g) < ND_rank(n))
908 GD_maxrank(g) = ND_rank(n);
909 if (GD_minrank(g) > ND_rank(n))
910 GD_minrank(g) = ND_rank(n);
911 if (minrk) {
912 ND_comp(n) = ND_comp(xn);
913 minrk[ND_comp(n)] = MIN(minrk[ND_comp(n)],ND_rank(n));
914 }
915 }
916 if (minrk) {
917 for (n = agfstnode(g); n; n = agnxtnode(g, n))
918 ND_rank(n) -= minrk[ND_comp(n)];
919 /* Non-uniform shifting, so recompute maxrank/minrank of root graph */
920 doRoot = 1;
921 }
922 else if (GD_minrank(g) > 0) { /* should never happen */
923 int delta = GD_minrank(g);
924 for (n = agfstnode(g); n; n = agnxtnode(g, n))
925 ND_rank(n) -= delta;
926 GD_minrank(g) -= delta;
927 GD_maxrank(g) -= delta;
928 }
929
930 setMinMax(g, doRoot);
931
932 /* release fastgraph memory from Xg */
933 for (n = agfstnode(Xg); n; n = agnxtnode(Xg, n)) {
934 free_list(ND_in(n));
935 free_list(ND_out(n));
936 }
937
938 free(ND_alg(agfstnode(g)));
939 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
940 ND_alg(n) = NULL;
941 }
942 free(minrk);
943}
944
945static void dfscc(graph_t * g, node_t * n, int cc)
946{
947 edge_t *e;
948 if (ND_comp(n) == 0) {
949 ND_comp(n) = cc;
950 for (e = agfstout(g, n); e; e = agnxtout(g, e))
951 dfscc(g, aghead(e), cc);
952 for (e = agfstin(g, n); e; e = agnxtin(g, e))
953 dfscc(g, agtail(e), cc);
954 }
955}
956
958{
959 int cc = 0;
960 node_t *n;
961
962 for (n = agfstnode(g); n; n = agnxtnode(g, n))
963 ND_comp(n) = 0;
964 for (n = agfstnode(g); n; n = agnxtnode(g, n))
965 if (ND_comp(n) == 0)
966 dfscc(g, n, ++cc);
967 if (cc > 1) {
968 node_t *root = makeXnode(g, ROOT);
969 int ncc = 1;
970 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
971 if (ND_comp(n) == ncc) {
972 (void) agedge(g, root, n, 0, 1);
973 ncc++;
974 }
975 }
976 }
977 return (cc);
978}
979
980static void add_fast_edges (graph_t * g)
981{
982 node_t *n;
983 edge_t *e;
984 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
985 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
986 elist_append(e, ND_out(n));
987 elist_append(e, ND_in(aghead(e)));
988 }
989 }
990}
991
992static void my_init_graph(Agraph_t *g, Agobj_t *graph, void *arg)
993{ int *sz = arg; (void)g; agbindrec(graph,"level graph rec",sz[0],true); }
994static void my_init_node(Agraph_t *g, Agobj_t *node, void *arg)
995{ int *sz = arg; (void)g; agbindrec(node,"level node rec",sz[1],true); }
996static void my_init_edge(Agraph_t *g, Agobj_t *edge, void *arg)
997{ int *sz = arg; (void)g; agbindrec(edge,"level edge rec",sz[2],true); }
998static Agcbdisc_t mydisc = { {my_init_graph,0,0}, {my_init_node,0,0}, {my_init_edge,0,0} };
999
1000int infosizes[] = {
1001 sizeof(Agraphinfo_t),
1002 sizeof(Agnodeinfo_t),
1003 sizeof(Agedgeinfo_t)
1004};
1005
1007 int ssize;
1008 int ncc, maxiter = INT_MAX;
1009 char *s;
1010 graph_t *Xg;
1011
1012 Last_node = NULL;
1013 Xg = agopen("level assignment constraints", Agstrictdirected, 0);
1014 agbindrec(Xg,"level graph rec",sizeof(Agraphinfo_t),true);
1016
1017 edgelabel_ranks(g);
1018
1019 if ((s = agget(g, "nslimit1")))
1020 maxiter = scale_clamp(agnnodes(g), atof(s));
1021 else
1022 maxiter = INT_MAX;
1023
1024 compile_samerank(g, 0);
1025 compile_nodes(g, Xg);
1026 compile_edges(g, Xg);
1027 compile_clusters(g, Xg, 0, 0);
1028 break_cycles(Xg);
1029 ncc = connect_components(Xg);
1030 add_fast_edges (Xg);
1031
1032 if ((s = agget(g, "searchsize")))
1033 ssize = atoi(s);
1034 else
1035 ssize = -1;
1036 rank2(Xg, 1, maxiter, ssize);
1037/* fastgr(Xg); */
1038 readout_levels(g, Xg, ncc);
1039#ifdef DEBUG
1040 fprintf (stderr, "Xg %d nodes %d edges\n", agnnodes(Xg), agnedges(Xg));
1041#endif
1042 agclose(Xg);
1043}
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define MIN(a, b)
Definition arith.h:28
void class1(graph_t *g)
Definition class1.c:62
#define sub(h, i)
Definition closest.c:67
bool mapbool(const char *p)
Definition utils.c:337
node_t * UF_union(node_t *u, node_t *v)
Definition utils.c:110
node_t * UF_find(node_t *n)
Definition utils.c:100
int maptoken(char *p, char **name, int *val)
Definition utils.c:311
void UF_singleton(node_t *u)
Definition utils.c:138
bool is_a_cluster(Agraph_t *g)
Definition utils.c:692
#define NORMAL
Definition const.h:24
#define MINRANK
Definition const.h:35
#define SOURCERANK
Definition const.h:36
#define LOCAL
Definition const.h:43
#define EDGE_LABEL
Definition const.h:176
#define NEW_RANK
Definition const.h:257
#define CLUSTER
Definition const.h:40
#define SAMERANK
Definition const.h:34
#define SINKRANK
Definition const.h:38
#define MAXRANK
Definition const.h:37
#define LEAFSET
Definition const.h:39
static Dtdisc_t constr
Definition constraint.c:51
void decompose(graph_t *g, int pass)
Definition decomp.c:114
Agraph_t * dot_root(void *p)
Definition dotinit.c:494
Agedge_t * virtual_edge(Agnode_t *, Agnode_t *, Agedge_t *)
Definition fastgr.c:172
#define G
Definition gdefs.h:7
int CL_type
Definition globals.h:60
Agsym_t * E_constr
Definition globals.h:98
static int Verbose
Definition gml2gv.c:22
static Dt_t * L
Definition gmlparse.c:92
void free(void *)
edge
Definition gmlparse.y:280
#define SIZE_MAX
Definition gmlscan.c:347
node NULL
Definition grammar.y:150
int agnedges(Agraph_t *g)
Definition graph.c:164
int agnnodes(Agraph_t *g)
Definition graph.c:158
char * agget(void *obj, char *name)
Definition attr.c:442
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:458
void agpushdisc(Agraph_t *g, Agcbdisc_t *disc, void *state)
Definition obj.c:201
#define ED_to_orig(e)
Definition types.h:598
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:260
#define ED_minlen(e)
Definition types.h:592
Agedge_t * agsubedge(Agraph_t *g, Agedge_t *e, int createflag)
Definition edge.c:355
Agedge_t * agnxtin(Agraph_t *g, Agedge_t *e)
Definition edge.c:68
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define agtail(e)
Definition cgraph.h:889
#define ED_weight(e)
Definition types.h:603
#define agfindedge(g, t, h)
Definition types.h:609
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:38
Agedge_t * agfstin(Agraph_t *g, Agnode_t *n)
Definition edge.c:54
#define ED_to_virt(e)
Definition types.h:599
void agwarningf(const char *fmt,...)
Definition agerror.c:173
void agerrorf(const char *fmt,...)
Definition agerror.c:165
#define GD_minrank(g)
Definition types.h:384
#define GD_maxrank(g)
Definition types.h:382
#define GD_maxset(g)
Definition types.h:383
#define GD_maxrep(g)
Definition types.h:387
#define GD_has_labels(g)
Definition types.h:368
#define GD_clust(g)
Definition types.h:360
#define GD_minrep(g)
Definition types.h:386
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:96
#define GD_flags(g)
Definition types.h:365
#define GD_parent(g)
Definition types.h:351
#define GD_nlist(g)
Definition types.h:393
Agdesc_t Agstrictdirected
strict directed. A strict graph cannot have multi-edges or self-arcs.
Definition graph.c:274
#define GD_minset(g)
Definition types.h:385
#define GD_n_cluster(g)
Definition types.h:389
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Definition graph.c:44
#define GD_level(g)
Definition types.h:352
#define GD_set_type(g)
Definition types.h:399
#define GD_leader(g)
Definition types.h:375
#define GD_comp(g)
Definition types.h:362
#define GD_ranksep(g)
Definition types.h:397
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:147
#define ND_rank(n)
Definition types.h:523
#define ND_prev(n)
Definition types.h:521
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
#define ND_next(n)
Definition types.h:510
#define ND_clust(n)
Definition types.h:489
#define ND_set(n)
Definition types.h:486
#define ND_alg(n)
Definition types.h:484
#define ND_node_type(n)
Definition types.h:511
#define ND_rep(n)
Definition types.h:496
#define ND_UF_size(n)
Definition types.h:487
#define ND_ranktype(n)
Definition types.h:524
#define ND_in(n)
Definition types.h:501
#define ND_out(n)
Definition types.h:515
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:19
int agcontains(Agraph_t *, void *obj)
returns non-zero if obj is a member of (sub)graph
Definition obj.c:234
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:88
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:77
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:82
static uint64_t id
Definition gv2gml.c:42
Agraph_t * graph(char *name)
Definition gv.cpp:31
Arithmetic helper functions.
static int scale_clamp(int original, double scale)
scale up or down a non-negative integer, clamping to [0, INT_MAX]
Definition gv_math.h:71
agxbuf * str
Definition htmlparse.c:97
void do_graph_label(graph_t *sg)
Set characteristics of graph label if it exists.
Definition input.c:837
#define ND_onstack(n)
Definition acyclic.c:29
#define ND_mark(n)
Definition acyclic.c:28
static Agedge_t * top(edge_stack_t *sp)
Definition tred.c:73
void acyclic(graph_t *g)
Definition acyclic.c:56
void reverse_edge(edge_t *e)
Definition acyclic.c:20
#define delta
Definition maze.c:133
int rank(graph_t *g, int balance, int maxiter)
Definition ns.c:997
int rank2(graph_t *g, int balance, int maxiter, int search_size)
Definition ns.c:919
#define BOTNODE
Definition rank.c:476
static void dfscc(graph_t *g, node_t *n, int cc)
Definition rank.c:945
static int make_new_cluster(graph_t *g, graph_t *subg)
Definition rank.c:160
static bool is_internal_to_cluster(edge_t *e)
Definition rank.c:645
static void compile_samerank(graph_t *ug, graph_t *parent_clust)
Definition rank.c:562
static void compile_clusters(graph_t *g, graph_t *Xg, node_t *top, node_t *bot)
Definition rank.c:777
static void compile_edges(graph_t *ug, graph_t *Xg)
Definition rank.c:736
#define STRONG_CLUSTER_WEIGHT
Definition rank.c:472
void dot_scan_ranks(graph_t *g)
Definition rank.c:202
#define ND_comp(n)
Definition rank.c:481
static graph_t * dot_lca(graph_t *c0, graph_t *c1)
Definition rank.c:634
static void expand_ranksets(graph_t *g)
Definition rank.c:392
void rank1(graph_t *g)
Definition rank.c:373
static int connect_components(graph_t *g)
Definition rank.c:957
static void node_induce(graph_t *par, graph_t *g)
Definition rank.c:172
static void readout_levels(graph_t *g, graph_t *Xg, int ncc)
Definition rank.c:889
static void setMinMax(graph_t *g, int doRoot)
Definition rank.c:855
static void collapse_cluster(graph_t *g, graph_t *subg)
Definition rank.c:254
static void cleanup1(graph_t *g)
Definition rank.c:46
static int rankset_kind(graph_t *g)
Definition rank.c:500
#define BACKWARD_PENALTY
Definition rank.c:471
static bool is_empty(graph_t *g)
Definition rank.c:490
static void renewlist(elist *L)
Definition rank.c:38
static void merge(edge_t *e, int minlen, int weight)
Definition rank.c:693
#define TOPNODE
Definition rank.c:475
static int minmax_edges2(graph_t *g, point slen)
Definition rank.c:348
void dot_rank(graph_t *g)
Definition rank.c:449
static void dfs(graph_t *g, node_t *v)
Definition rank.c:817
static void collapse_rankset(graph_t *g, graph_t *subg, int kind)
Definition rank.c:106
bool is_cluster(graph_t *g)
Definition rank.c:460
#define ROOT
Definition rank.c:474
static point minmax_edges(graph_t *g)
Definition rank.c:316
static node_t * union_one(node_t *leader, node_t *n)
Definition rank.c:541
int infosizes[]
Definition rank.c:1000
static void find_clusters(graph_t *g)
Definition rank.c:292
static void collapse_sets(graph_t *rg, graph_t *g)
Definition rank.c:273
static bool is_nonconstraint(edge_t *e)
Definition rank.c:519
static void strong(graph_t *g, node_t *t, node_t *h, edge_t *orig)
Definition rank.c:699
static void set_parent(graph_t *g, graph_t *p)
Definition rank.c:483
static node_t * Last_node
Definition rank.c:658
static void my_init_graph(Agraph_t *g, Agobj_t *graph, void *arg)
Definition rank.c:992
static node_t * find(node_t *n)
Definition rank.c:530
static node_t * makeXnode(graph_t *G, char *name)
Definition rank.c:659
static void reverse_edge2(graph_t *g, edge_t *e)
Definition rank.c:806
static Agcbdisc_t mydisc
Definition rank.c:998
static void add_fast_edges(graph_t *g)
Definition rank.c:980
static void break_cycles(graph_t *g)
Definition rank.c:839
static void dot1_rank(graph_t *g)
Definition rank.c:429
static void compile_nodes(graph_t *g, graph_t *Xg)
Definition rank.c:677
static bool is_a_strong_cluster(graph_t *g)
Definition rank.c:494
#define NORANK
Definition rank.c:473
static void dot2_rank(graph_t *g)
Definition rank.c:1006
static void weak(graph_t *g, node_t *t, node_t *h, edge_t *orig)
Definition rank.c:710
static int rank_set_class(graph_t *g)
Definition rank.c:145
static void cluster_leader(graph_t *clust)
Definition rank.c:223
static void my_init_node(Agraph_t *g, Agobj_t *node, void *arg)
Definition rank.c:994
static void edgelabel_ranks(graph_t *g)
Definition rank.c:91
static void my_init_edge(Agraph_t *g, Agobj_t *edge, void *arg)
Definition rank.c:996
static node_t * union_all(graph_t *g)
Definition rank.c:549
static void set_minmax(graph_t *g)
Definition rank.c:302
client event callbacks, used in Agcbstack_s
Definition cgraph.h:388
Agobj_t base
Definition cgraph.h:269
a generic header of Agraph_s, Agnode_s and Agedge_s
Definition cgraph.h:210
Agrec_t * data
stores programmer-defined data, access with AGDATA
Definition cgraph.h:212
graph or subgraph
Definition cgraph.h:425
Definition types.h:251
Definition geom.h:27
int y
Definition geom.h:27
int x
Definition geom.h:27
#define free_list(L)
Definition types.h:272
#define elist_append(item, L)
Definition types.h:261
#define alloc_elist(n, L)
Definition types.h:267
Definition grammar.c:94
#define MAX(a, b)
Definition write.c:31