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