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