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