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