1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3
4 /*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14
15 /*
16 * Rank the nodes of a directed graph, subject to user-defined
17 * sets of nodes to be kept on the same, min, or max rank.
18 * The temporary acyclic fast graph is constructed and ranked
19 * by a network-simplex technique. Then ranks are propagated
20 * to non-leader nodes and temporary edges are deleted.
21 * Leaf nodes and top-level clusters are left collapsed, though.
22 * Assigns global minrank and maxrank of graph and all clusters.
23 *
24 * TODO: safety code. must not be in two clusters at same level.
25 * must not be in same/min/max/rank and a cluster at the same time.
26 * watch out for interactions between leaves and clusters.
27 */
28
29 #include "dot.h"
30
31 static void dot1_rank(graph_t * g, aspect_t* asp);
32 static void dot2_rank(graph_t * g, aspect_t* asp);
33
34 static void
renewlist(elist * L)35 renewlist(elist * L)
36 {
37 int i;
38 for (i = L->size; i >= 0; i--)
39 L->list[i] = NULL;
40 L->size = 0;
41 }
42
43 static void
cleanup1(graph_t * g)44 cleanup1(graph_t * g)
45 {
46 node_t *n;
47 edge_t *e, *f;
48 int c;
49
50 for (c = 0; c < GD_comp(g).size; c++) {
51 GD_nlist(g) = GD_comp(g).list[c];
52 for (n = GD_nlist(g); n; n = ND_next(n)) {
53 renewlist(&ND_in(n));
54 renewlist(&ND_out(n));
55 ND_mark(n) = FALSE;
56 }
57 }
58 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
59 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
60 f = ED_to_virt(e);
61 /* Null out any other references to f to make sure we don't
62 * handle it a second time. For example, parallel multiedges
63 * share a virtual edge.
64 */
65 if (f && (e != ED_to_orig(f))) {
66 ED_to_virt(e) = NULL;
67 }
68 }
69 }
70 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
71 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
72 f = ED_to_virt(e);
73 if (f && ED_to_orig(f) == e) {
74 free(f->base.data);
75 free(f);
76 ED_to_virt(e) = NULL;
77 }
78 }
79 }
80 free(GD_comp(g).list);
81 GD_comp(g).list = NULL;
82 GD_comp(g).size = 0;
83 }
84
85 /*static void
86 cleanup1(graph_t * g)
87 {
88 node_t *n;
89 edge_t *e, *f;
90 int c;
91
92 for (c = 0; c < GD_comp(g).size; c++) {
93 GD_nlist(g) = GD_comp(g).list[c];
94 for (n = GD_nlist(g); n; n = ND_next(n)) {
95 renewlist(&ND_in(n));
96 renewlist(&ND_out(n));
97 ND_mark(n) = FALSE;
98 }
99 }
100 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
101 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
102 f = ED_to_virt(e);
103 * Null out any other references to f to make sure we don't
104 * handle it a second time. For example, parallel multiedges
105 * share a virtual edge.
106 *
107 if (f && (e == ED_to_orig(f))) {
108 edge_t *e1, *f1;
109 node_t *n1;
110 for (n1 = agfstnode(g); n1; n1 = agnxtnode(g, n1)) {
111 for (e1 = agfstout(g, n1); e1; e1 = agnxtout(g, e1)) {
112 if (e != e1) {
113 f1 = ED_to_virt(e1);
114 if (f1 && (f == f1)) {
115 ED_to_virt(e1) = NULL;
116 }
117 }
118 }
119 }
120 free(f->base.data);
121 free(f);
122 }
123 ED_to_virt(e) = NULL;
124 }
125 }
126 free(GD_comp(g).list);
127 GD_comp(g).list = NULL;
128 GD_comp(g).size = 0;
129 }
130 */
131
132 /* When there are edge labels, extra ranks are reserved here for the virtual
133 * nodes of the labels. This is done by doubling the input edge lengths.
134 * The input rank separation is adjusted to compensate.
135 */
136 static void
edgelabel_ranks(graph_t * g)137 edgelabel_ranks(graph_t * g)
138 {
139 node_t *n;
140 edge_t *e;
141
142 if (GD_has_labels(g) & EDGE_LABEL) {
143 for (n = agfstnode(g); n; n = agnxtnode(g, n))
144 for (e = agfstout(g, n); e; e = agnxtout(g, e))
145 ED_minlen(e) *= 2;
146 GD_ranksep(g) = (GD_ranksep(g) + 1) / 2;
147 }
148 }
149
150 /* Merge the nodes of a min, max, or same rank set. */
151 static void
collapse_rankset(graph_t * g,graph_t * subg,int kind)152 collapse_rankset(graph_t * g, graph_t * subg, int kind)
153 {
154 node_t *u, *v;
155
156 u = v = agfstnode(subg);
157 if (u) {
158 ND_ranktype(u) = kind;
159 while ((v = agnxtnode(subg, v))) {
160 UF_union(u, v);
161 ND_ranktype(v) = ND_ranktype(u);
162 }
163 switch (kind) {
164 case MINRANK:
165 case SOURCERANK:
166 if (GD_minset(g) == NULL)
167 GD_minset(g) = u;
168 else
169 GD_minset(g) = UF_union(GD_minset(g), u);
170 break;
171 case MAXRANK:
172 case SINKRANK:
173 if (GD_maxset(g) == NULL)
174 GD_maxset(g) = u;
175 else
176 GD_maxset(g) = UF_union(GD_maxset(g), u);
177 break;
178 }
179 switch (kind) {
180 case SOURCERANK:
181 ND_ranktype(GD_minset(g)) = kind;
182 break;
183 case SINKRANK:
184 ND_ranktype(GD_maxset(g)) = kind;
185 break;
186 }
187 }
188 }
189
190 static int
rank_set_class(graph_t * g)191 rank_set_class(graph_t * g)
192 {
193 static char *name[] = { "same", "min", "source", "max", "sink", NULL };
194 static int class[] =
195 { SAMERANK, MINRANK, SOURCERANK, MAXRANK, SINKRANK, 0 };
196 int val;
197
198 if (is_cluster(g))
199 return CLUSTER;
200 val = maptoken(agget(g, "rank"), name, class);
201 GD_set_type(g) = val;
202 return val;
203 }
204
205 static int
make_new_cluster(graph_t * g,graph_t * subg)206 make_new_cluster(graph_t * g, graph_t * subg)
207 {
208 int cno;
209 cno = ++(GD_n_cluster(g));
210 GD_clust(g) = ZALLOC(cno + 1, GD_clust(g), graph_t *, GD_n_cluster(g));
211 GD_clust(g)[cno] = subg;
212 do_graph_label(subg);
213 return cno;
214 }
215
216 static void
node_induce(graph_t * par,graph_t * g)217 node_induce(graph_t * par, graph_t * g)
218 {
219 node_t *n, *nn;
220 edge_t *e;
221 int i;
222
223 /* enforce that a node is in at most one cluster at this level */
224 for (n = agfstnode(g); n; n = nn) {
225 nn = agnxtnode(g, n);
226 if (ND_ranktype(n)) {
227 agdelete(g, n);
228 continue;
229 }
230 for (i = 1; i < GD_n_cluster(par); i++)
231 if (agcontains(GD_clust(par)[i], n))
232 break;
233 if (i < GD_n_cluster(par))
234 agdelete(g, n);
235 ND_clust(n) = NULL;
236 }
237
238 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
239 for (e = agfstout(dot_root(g), n); e; e = agnxtout(dot_root(g), e)) {
240 if (agcontains(g, aghead(e)))
241 agsubedge(g,e,1);
242 }
243 }
244 }
245
246 void
dot_scan_ranks(graph_t * g)247 dot_scan_ranks(graph_t * g)
248 {
249 node_t *n, *leader = NULL;
250 GD_minrank(g) = MAXSHORT;
251 GD_maxrank(g) = -1;
252 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
253 if (GD_maxrank(g) < ND_rank(n))
254 GD_maxrank(g) = ND_rank(n);
255 if (GD_minrank(g) > ND_rank(n))
256 GD_minrank(g) = ND_rank(n);
257 if (leader == NULL)
258 leader = n;
259 else {
260 if (ND_rank(n) < ND_rank(leader))
261 leader = n;
262 }
263 }
264 GD_leader(g) = leader;
265 }
266
267 static void
cluster_leader(graph_t * clust)268 cluster_leader(graph_t * clust)
269 {
270 node_t *leader, *n;
271 int maxrank = 0;
272
273 /* find number of ranks and select a leader */
274 leader = NULL;
275 for (n = GD_nlist(clust); n; n = ND_next(n)) {
276 if ((ND_rank(n) == 0) && (ND_node_type(n) == NORMAL))
277 leader = n;
278 if (maxrank < ND_rank(n))
279 maxrank = ND_rank(n);
280 }
281 assert(leader != NULL);
282 GD_leader(clust) = leader;
283
284 for (n = agfstnode(clust); n; n = agnxtnode(clust, n)) {
285 assert((ND_UF_size(n) <= 1) || (n == leader));
286 UF_union(n, leader);
287 ND_ranktype(n) = CLUSTER;
288 }
289 }
290
291 /*
292 * A cluster is collapsed in three steps.
293 * 1) The nodes of the cluster are ranked locally.
294 * 2) The cluster is collapsed into one node on the least rank.
295 * 3) In class1(), any inter-cluster edges are converted using
296 * the "virtual node + 2 edges" trick.
297 */
298 static void
collapse_cluster(graph_t * g,graph_t * subg)299 collapse_cluster(graph_t * g, graph_t * subg)
300 {
301 if (GD_parent(subg)) {
302 return;
303 }
304 GD_parent(subg) = g;
305 node_induce(g, subg);
306 if (agfstnode(subg) == NULL)
307 return;
308 make_new_cluster(g, subg);
309 if (CL_type == LOCAL) {
310 dot1_rank(subg, 0);
311 cluster_leader(subg);
312 } else
313 dot_scan_ranks(subg);
314 }
315
316 /* Execute union commands for "same rank" subgraphs and clusters. */
317 static void
collapse_sets(graph_t * rg,graph_t * g)318 collapse_sets(graph_t *rg, graph_t *g)
319 {
320 int c;
321 graph_t *subg;
322 #ifdef OBSOLETE
323 node_t *n;
324 #endif
325
326 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
327 c = rank_set_class(subg);
328 if (c) {
329 if ((c == CLUSTER) && CL_type == LOCAL)
330 collapse_cluster(rg, subg);
331 else
332 collapse_rankset(rg, subg, c);
333 }
334 else collapse_sets(rg, subg);
335
336 #ifdef OBSOLETE
337 Collapsing leaves is currently obsolete
338
339 /* mark nodes with ordered edges so their leaves are not collapsed */
340 if (agget(subg, "ordering"))
341 for (n = agfstnode(subg); n; n = agnxtnode(subg, n))
342 ND_order(n) = 1;
343 #endif
344 }
345 }
346
347 static void
find_clusters(graph_t * g)348 find_clusters(graph_t * g)
349 {
350 graph_t *subg;
351 for (subg = agfstsubg(dot_root(g)); subg; subg = agnxtsubg(subg)) {
352 if (GD_set_type(subg) == CLUSTER)
353 collapse_cluster(g, subg);
354 }
355 }
356
357 static void
set_minmax(graph_t * g)358 set_minmax(graph_t * g)
359 {
360 int c;
361
362 GD_minrank(g) += ND_rank(GD_leader(g));
363 GD_maxrank(g) += ND_rank(GD_leader(g));
364 for (c = 1; c <= GD_n_cluster(g); c++)
365 set_minmax(GD_clust(g)[c]);
366 }
367
368 /* To ensure that min and max rank nodes always have the intended rank
369 * assignment, reverse any incompatible edges.
370 */
371 static point
minmax_edges(graph_t * g)372 minmax_edges(graph_t * g)
373 {
374 node_t *n;
375 edge_t *e;
376 point slen;
377
378 slen.x = slen.y = 0;
379 if ((GD_maxset(g) == NULL) && (GD_minset(g) == NULL))
380 return slen;
381 if (GD_minset(g) != NULL)
382 GD_minset(g) = UF_find(GD_minset(g));
383 if (GD_maxset(g) != NULL)
384 GD_maxset(g) = UF_find(GD_maxset(g));
385
386 if ((n = GD_maxset(g))) {
387 slen.y = (ND_ranktype(GD_maxset(g)) == SINKRANK);
388 while ((e = ND_out(n).list[0])) {
389 assert(aghead(e) == UF_find(aghead(e)));
390 reverse_edge(e);
391 }
392 }
393 if ((n = GD_minset(g))) {
394 slen.x = (ND_ranktype(GD_minset(g)) == SOURCERANK);
395 while ((e = ND_in(n).list[0])) {
396 assert(agtail(e) == UF_find(agtail(e)));
397 reverse_edge(e);
398 }
399 }
400 return slen;
401 }
402
403 static int
minmax_edges2(graph_t * g,point slen)404 minmax_edges2(graph_t * g, point slen)
405 {
406 node_t *n;
407 edge_t *e = 0;
408
409 if ((GD_maxset(g)) || (GD_minset(g))) {
410 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
411 if (n != UF_find(n))
412 continue;
413 if ((ND_out(n).size == 0) && GD_maxset(g) && (n != GD_maxset(g))) {
414 e = virtual_edge(n, GD_maxset(g), NULL);
415 ED_minlen(e) = slen.y;
416 ED_weight(e) = 0;
417 }
418 if ((ND_in(n).size == 0) && GD_minset(g) && (n != GD_minset(g))) {
419 e = virtual_edge(GD_minset(g), n, NULL);
420 ED_minlen(e) = slen.x;
421 ED_weight(e) = 0;
422 }
423 }
424 }
425 return (e != 0);
426 }
427
428 /* Run the network simplex algorithm on each component. */
rank1(graph_t * g)429 void rank1(graph_t * g)
430 {
431 int maxiter = INT_MAX;
432 int c;
433 char *s;
434
435 if ((s = agget(g, "nslimit1")))
436 maxiter = atof(s) * agnnodes(g);
437 for (c = 0; c < GD_comp(g).size; c++) {
438 GD_nlist(g) = GD_comp(g).list[c];
439 rank(g, (GD_n_cluster(g) == 0 ? 1 : 0), maxiter); /* TB balance */
440 }
441 }
442
443 /*
444 * Assigns ranks of non-leader nodes.
445 * Expands same, min, max rank sets.
446 * Leaf sets and clusters remain merged.
447 * Sets minrank and maxrank appropriately.
448 */
expand_ranksets(graph_t * g,aspect_t * asp)449 static void expand_ranksets(graph_t * g, aspect_t* asp)
450 {
451 int c;
452 node_t *n, *leader;
453
454 if ((n = agfstnode(g))) {
455 GD_minrank(g) = MAXSHORT;
456 GD_maxrank(g) = -1;
457 while (n) {
458 leader = UF_find(n);
459 /* The following works because ND_rank(n) == 0 if n is not in a
460 * cluster, and ND_rank(n) = the local rank offset if n is in
461 * a cluster. */
462 if ((leader != n) && (!asp || (ND_rank(n) == 0)))
463 ND_rank(n) += ND_rank(leader);
464
465 if (GD_maxrank(g) < ND_rank(n))
466 GD_maxrank(g) = ND_rank(n);
467 if (GD_minrank(g) > ND_rank(n))
468 GD_minrank(g) = ND_rank(n);
469
470 if (ND_ranktype(n) && (ND_ranktype(n) != LEAFSET))
471 UF_singleton(n);
472 n = agnxtnode(g, n);
473 }
474 if (g == dot_root(g)) {
475 if (CL_type == LOCAL) {
476 for (c = 1; c <= GD_n_cluster(g); c++)
477 set_minmax(GD_clust(g)[c]);
478 } else {
479 find_clusters(g);
480 }
481 }
482 } else {
483 GD_minrank(g) = GD_maxrank(g) = 0;
484 }
485 }
486
487 #ifdef ALLOW_LEVELS
488 void
setRanks(graph_t * g,attrsym_t * lsym)489 setRanks (graph_t* g, attrsym_t* lsym)
490 {
491 node_t* n;
492 char* s;
493 char* ep;
494 long v;
495
496 for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
497 s = agxget (n, lsym);
498 v = strtol (s, &ep, 10);
499 if (ep == s)
500 agerr(AGWARN, "no level attribute for node \"%s\"\n", agnameof(n));
501 ND_rank(n) = v;
502 }
503 }
504 #endif
505
506 #ifdef UNUSED
507 static node_t **virtualEdgeHeadList = NULL;
508 static node_t **virtualEdgeTailList = NULL;
509 static int nVirtualEdges = 0;
510
511 static void
saveVirtualEdges(graph_t * g)512 saveVirtualEdges(graph_t *g)
513 {
514 edge_t *e;
515 node_t *n;
516 int cnt = 0;
517 int lc;
518
519 if (virtualEdgeHeadList != NULL) {
520 free(virtualEdgeHeadList);
521 }
522 if (virtualEdgeTailList != NULL) {
523 free(virtualEdgeTailList);
524 }
525
526 /* allocate memory */
527 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
528 for (lc = 0; lc < ND_in(n).size; lc++) {
529 e = ND_in(n).list[lc];
530 if (ED_edge_type(e) == VIRTUAL) cnt++;
531 }
532 }
533
534 nVirtualEdges = cnt;
535 virtualEdgeHeadList = N_GNEW(cnt, node_t*);
536 virtualEdgeTailList = N_GNEW(cnt, node_t*);
537
538 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
539 for (lc = 0, cnt = 0; lc < ND_in(n).size; lc++) {
540 e = ND_in(n).list[lc];
541 if (ED_edge_type(e) == VIRTUAL) {
542 virtualEdgeHeadList[cnt] = e->head;
543 virtualEdgeTailList[cnt] = e->tail;
544 if (Verbose)
545 printf("saved virtual edge: %s->%s\n",
546 virtualEdgeTailList[cnt]->name,
547 virtualEdgeHeadList[cnt]->name);
548 cnt++;
549 }
550 }
551 }
552 }
553
554 static void
restoreVirtualEdges(graph_t * g)555 restoreVirtualEdges(graph_t *g)
556 {
557 int i;
558 edge_t e;
559
560 for (i = 0; i < nVirtualEdges; i++) {
561 if (virtualEdgeTailList[i] && virtualEdgeHeadList[i]) {
562 if (Verbose)
563 printf("restoring virtual edge: %s->%s\n",
564 virtualEdgeTailList[i]->name, virtualEdgeHeadList[i]->name);
565 virtual_edge(virtualEdgeTailList[i], virtualEdgeHeadList[i], NULL);
566 }
567 }
568 if (Verbose)
569 printf("restored %d virt edges\n", nVirtualEdges);
570 }
571 #endif
572
573 /* dot1_rank:
574 * asp != NULL => g is root
575 */
dot1_rank(graph_t * g,aspect_t * asp)576 static void dot1_rank(graph_t * g, aspect_t* asp)
577 {
578 point p;
579 #ifdef ALLOW_LEVELS
580 attrsym_t* N_level;
581 #endif
582 edgelabel_ranks(g);
583
584 if (asp) {
585 init_UF_size(g);
586 initEdgeTypes(g);
587 }
588
589 collapse_sets(g,g);
590 /*collapse_leaves(g); */
591 class1(g);
592 p = minmax_edges(g);
593 decompose(g, 0);
594 if (asp && ((GD_comp(g).size > 1)||(GD_n_cluster(g) > 0))) {
595 asp->badGraph = 1;
596 asp = NULL;
597 }
598 acyclic(g);
599 if (minmax_edges2(g, p))
600 decompose(g, 0);
601 #ifdef ALLOW_LEVELS
602 if ((N_level = agattr(g,AGNODE,"level",NULL)))
603 setRanks(g, N_level);
604 else
605 #endif
606
607 if (asp)
608 rank3(g, asp);
609 else
610 rank1(g);
611
612 expand_ranksets(g, asp);
613 cleanup1(g);
614 }
615
dot_rank(graph_t * g,aspect_t * asp)616 void dot_rank(graph_t * g, aspect_t* asp)
617 {
618 if (agget (g, "newrank")) {
619 GD_flags(g) |= NEW_RANK;
620 dot2_rank (g, asp);
621 }
622 else
623 dot1_rank (g, asp);
624 if (Verbose)
625 fprintf (stderr, "Maxrank = %d, minrank = %d\n", GD_maxrank(g), GD_minrank(g));
626 }
627
is_cluster(graph_t * g)628 int is_cluster(graph_t * g)
629 {
630 //return (strncmp(agnameof(g), "cluster", 7) == 0);
631 return is_a_cluster(g); // from utils.c
632 }
633
634 #ifdef OBSOLETE
635 static node_t*
merge_leaves(graph_t * g,node_t * cur,node_t * new)636 merge_leaves(graph_t * g, node_t * cur, node_t * new)
637 {
638 node_t *rv;
639
640 if (cur == NULL)
641 rv = new;
642 else {
643 rv = UF_union(cur, new);
644 ND_ht(rv) = MAX(ND_ht(cur), ND_ht(new));
645 ND_lw(rv) = ND_lw(cur) + ND_lw(new) + GD_nodesep(g) / 2;
646 ND_rw(rv) = ND_rw(cur) + ND_rw(new) + GD_nodesep(g) / 2;
647 }
648 return rv;
649 }
650
651 static void
potential_leaf(graph_t * g,edge_t * e,node_t * leaf)652 potential_leaf(graph_t * g, edge_t * e, node_t * leaf)
653 {
654 node_t *par;
655
656 if ((ED_tail_port(e).p.x) || (ED_head_port(e).p.x))
657 return;
658 if ((ED_minlen(e) != 1) || (ND_order(agtail(e)) > 0))
659 return;
660 par = ((leaf != aghead(e)) ? aghead(e) : agtail(e));
661 ND_ranktype(leaf) = LEAFSET;
662 if (par == agtail(e))
663 GD_outleaf(par) = merge_leaves(g, GD_outleaf(par), leaf);
664 else
665 GD_inleaf(par) = merge_leaves(g, GD_inleaf(par), leaf);
666 }
667
668 static void
collapse_leaves(graph_t * g)669 collapse_leaves(graph_t * g)
670 {
671 node_t *n;
672 edge_t *e;
673
674 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
675
676 /* consider n as a potential leaf of some other node. */
677 if ((ND_ranktype(n) != NOCMD) || (ND_order(n)))
678 continue;
679 if (agfstout(g, n) == NULL) {
680 if ((e = agfstin(g, n)) && (agnxtin(g, e) == NULL)) {
681 potential_leaf(g, AGMKOUT(e), n);
682 continue;
683 }
684 }
685 if (agfstin(g, n) == NULL) {
686 if ((e = agfstout(g, n)) && (agnxtout(g, e) == NULL)) {
687 potential_leaf(g, e, n);
688 continue;
689 }
690 }
691 }
692 }
693 #endif
694
695 /* new ranking code:
696 * Allows more constraints
697 * Copy of level.c in dotgen2
698 * Many of the utility functions are simpler or gone with
699 * cgraph library.
700 */
701 #define BACKWARD_PENALTY 1000
702 #define STRONG_CLUSTER_WEIGHT 1000
703 #define NORANK 6
704 #define ROOT "\177root"
705 #define TOPNODE "\177top"
706 #define BOTNODE "\177bot"
707
708 /* hops is not used in dot, so we overload it to
709 * contain the index of the connected component
710 */
711 #define ND_comp(n) ND_hops(n)
712
713 extern int rank2(Agraph_t *, int, int, int);
714
set_parent(graph_t * g,graph_t * p)715 static void set_parent(graph_t* g, graph_t* p)
716 {
717 GD_parent(g) = p;
718 make_new_cluster(p, g);
719 node_induce(p, g);
720 }
721
is_empty(graph_t * g)722 static int is_empty(graph_t * g)
723 {
724 return (!agfstnode(g));
725 }
726
is_a_strong_cluster(graph_t * g)727 static int is_a_strong_cluster(graph_t * g)
728 {
729 int rv;
730 char *str = agget(g, "compact");
731 /* rv = mapBool((str), TRUE); */
732 rv = mapBool((str), FALSE);
733 return rv;
734 }
735
rankset_kind(graph_t * g)736 static int rankset_kind(graph_t * g)
737 {
738 char *str = agget(g, "rank");
739
740 if (str && str[0]) {
741 if (!strcmp(str, "min"))
742 return MINRANK;
743 if (!strcmp(str, "source"))
744 return SOURCERANK;
745 if (!strcmp(str, "max"))
746 return MAXRANK;
747 if (!strcmp(str, "sink"))
748 return SINKRANK;
749 if (!strcmp(str, "same"))
750 return SAMERANK;
751 }
752 return NORANK;
753 }
754
is_nonconstraint(edge_t * e)755 static int is_nonconstraint(edge_t * e)
756 {
757 char *constr;
758
759 if (E_constr && (constr = agxget(e, E_constr))) {
760 if (constr[0] && mapbool(constr) == FALSE)
761 return TRUE;
762 }
763 return FALSE;
764 }
765
find(node_t * n)766 static node_t *find(node_t * n)
767 {
768 node_t *set;
769 if ((set = ND_set(n))) {
770 if (set != n)
771 set = ND_set(n) = find(set);
772 } else
773 set = ND_set(n) = n;
774 return set;
775 }
776
union_one(node_t * leader,node_t * n)777 static node_t *union_one(node_t * leader, node_t * n)
778 {
779 if (n)
780 return (ND_set(find(n)) = find(leader));
781 else
782 return leader;
783 }
784
union_all(graph_t * g)785 static node_t *union_all(graph_t * g)
786 {
787 node_t *n, *leader;
788
789 n = agfstnode(g);
790 if (!n)
791 return n;
792 leader = find(n);
793 while ((n = agnxtnode(g, n)))
794 union_one(leader, n);
795 return leader;
796 }
797
compile_samerank(graph_t * ug,graph_t * parent_clust)798 static void compile_samerank(graph_t * ug, graph_t * parent_clust)
799 {
800 graph_t *s; /* subgraph being scanned */
801 graph_t *clust; /* cluster that contains the rankset */
802 node_t *n, *leader;
803
804 if (is_empty(ug))
805 return;
806 if (is_a_cluster(ug)) {
807 clust = ug;
808 if (parent_clust) {
809 GD_level(ug) = GD_level(parent_clust) + 1;
810 set_parent(ug, parent_clust);
811 }
812 else
813 GD_level(ug) = 0;
814 } else
815 clust = parent_clust;
816
817 /* process subgraphs of this subgraph */
818 for (s = agfstsubg(ug); s; s = agnxtsubg(s))
819 compile_samerank(s, clust);
820
821 /* process this subgraph as a cluster */
822 if (is_a_cluster(ug)) {
823 for (n = agfstnode(ug); n; n = agnxtnode(ug, n)) {
824 if (ND_clust(n) == 0)
825 ND_clust(n) = ug;
826 #ifdef DEBUG
827 fprintf(stderr, "(%s) %s %p\n", agnameof(ug), agnameof(n),
828 ND_clust(n));
829 #endif
830 }
831 }
832
833 /* process this subgraph as a rankset */
834 switch (rankset_kind(ug)) {
835 case SOURCERANK:
836 GD_has_sourcerank(clust) = TRUE; /* fall through */
837 case MINRANK:
838 leader = union_all(ug);
839 GD_minrep(clust) = union_one(leader, GD_minrep(clust));
840 break;
841 case SINKRANK:
842 GD_has_sinkrank(clust) = TRUE; /* fall through */
843 case MAXRANK:
844 leader = union_all(ug);
845 GD_maxrep(clust) = union_one(leader, GD_maxrep(clust));
846 break;
847 case SAMERANK:
848 leader = union_all(ug);
849 /* do we need to record these ranksets? */
850 break;
851 case NORANK:
852 break;
853 default: /* unrecognized - warn and do nothing */
854 agerr(AGWARN, "%s has unrecognized rank=%s", agnameof(ug),
855 agget(ug, "rank"));
856 }
857
858 /* a cluster may become degenerate */
859 if (is_a_cluster(ug) && GD_minrep(ug)) {
860 if (GD_minrep(ug) == GD_maxrep(ug)) {
861 node_t *up = union_all(ug);
862 GD_minrep(ug) = up;
863 GD_maxrep(ug) = up;
864 }
865 }
866 }
867
dot_lca(graph_t * c0,graph_t * c1)868 static graph_t *dot_lca(graph_t * c0, graph_t * c1)
869 {
870 while (c0 != c1) {
871 if (GD_level(c0) >= GD_level(c1))
872 c0 = GD_parent(c0);
873 else
874 c1 = GD_parent(c1);
875 }
876 return c0;
877 }
878
is_internal_to_cluster(edge_t * e)879 static int is_internal_to_cluster(edge_t * e)
880 {
881 graph_t *par, *ct, *ch;
882 ct = ND_clust(agtail(e));
883 ch = ND_clust(aghead(e));
884 if (ct == ch)
885 return TRUE;
886 par = dot_lca(ct, ch);
887 /* if (par == agroot(par)) */
888 /* return FALSE; */
889 if ((par == ct) || (par == ch))
890 return TRUE;
891 return FALSE;
892 }
893
894 static node_t* Last_node;
makeXnode(graph_t * G,char * name)895 static node_t* makeXnode (graph_t* G, char* name)
896 {
897 node_t *n = agnode(G, name, 1);
898 alloc_elist(4, ND_in(n));
899 alloc_elist(4, ND_out(n));
900 if (Last_node) {
901 ND_prev(n) = Last_node;
902 ND_next(Last_node) = n;
903 } else {
904 ND_prev(n) = NULL;
905 GD_nlist(G) = n;
906 }
907 Last_node = n;
908 ND_next(n) = NULL;
909
910 return n;
911 }
912
compile_nodes(graph_t * g,graph_t * Xg)913 static void compile_nodes(graph_t * g, graph_t * Xg)
914 {
915 /* build variables */
916 node_t *n;
917
918 Last_node = NULL;
919 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
920 if (find(n) == n)
921 ND_rep(n) = makeXnode (Xg, agnameof(n));
922 }
923 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
924 if (ND_rep(n) == 0)
925 ND_rep(n) = ND_rep(find(n));
926 }
927 }
928
merge(edge_t * e,int minlen,int weight)929 static void merge(edge_t * e, int minlen, int weight)
930 {
931 ED_minlen(e) = MAX(ED_minlen(e), minlen);
932 ED_weight(e) += weight;
933 }
934
strong(graph_t * g,node_t * t,node_t * h,edge_t * orig)935 static void strong(graph_t * g, node_t * t, node_t * h, edge_t * orig)
936 {
937 edge_t *e;
938 if ((e = agfindedge(g, t, h)) ||
939 (e = agfindedge(g, h, t)) || (e = agedge(g, t, h, 0, 1)))
940 merge(e, ED_minlen(orig), ED_weight(orig));
941 else
942 agerr(AGERR, "ranking: failure to create strong constraint edge between nodes %s and %s\n",
943 agnameof(t), agnameof(h));
944 }
945
weak(graph_t * g,node_t * t,node_t * h,edge_t * orig)946 static void weak(graph_t * g, node_t * t, node_t * h, edge_t * orig)
947 {
948 node_t *v;
949 edge_t *e, *f;
950 static int id;
951 char buf[100];
952
953 for (e = agfstin(g, t); e; e = agnxtin(g, e)) {
954 /* merge with existing weak edge (e,f) */
955 v = agtail(e);
956 if ((f = agfstout(g, v)) && (aghead(f) == h)) {
957 return;
958 }
959 }
960 if (!e) {
961 sprintf (buf, "_weak_%d", id++);
962 v = makeXnode(g, buf);
963 e = agedge(g, v, t, 0, 1);
964 f = agedge(g, v, h, 0, 1);
965 }
966 ED_minlen(e) = MAX(ED_minlen(e), 0); /* effectively a nop */
967 ED_weight(e) += ED_weight(orig) * BACKWARD_PENALTY;
968 ED_minlen(f) = MAX(ED_minlen(f), ED_minlen(orig));
969 ED_weight(f) += ED_weight(orig);
970 }
971
compile_edges(graph_t * ug,graph_t * Xg)972 static void compile_edges(graph_t * ug, graph_t * Xg)
973 {
974 node_t *n;
975 edge_t *e;
976 node_t *Xt, *Xh;
977 graph_t *tc, *hc;
978
979 /* build edge constraints */
980 for (n = agfstnode(ug); n; n = agnxtnode(ug, n)) {
981 Xt = ND_rep(n);
982 for (e = agfstout(ug, n); e; e = agnxtout(ug, e)) {
983 if (is_nonconstraint(e))
984 continue;
985 Xh = ND_rep(find(aghead(e)));
986 if (Xt == Xh)
987 continue;
988
989 tc = ND_clust(agtail(e));
990 hc = ND_clust(aghead(e));
991
992 if (is_internal_to_cluster(e)) {
993 /* determine if graph requires reversed edge */
994 if ((find(agtail(e)) == GD_maxrep(ND_clust(agtail(e))))
995 || (find(aghead(e)) == GD_minrep(ND_clust(aghead(e))))) {
996 node_t *temp = Xt;
997 Xt = Xh;
998 Xh = temp;
999 }
1000 strong(Xg, Xt, Xh, e);
1001 } else {
1002 if (is_a_strong_cluster(tc) || is_a_strong_cluster(hc))
1003 weak(Xg, Xt, Xh, e);
1004 else
1005 strong(Xg, Xt, Xh, e);
1006 }
1007 }
1008 }
1009 }
1010
compile_clusters(graph_t * g,graph_t * Xg,node_t * top,node_t * bot)1011 static void compile_clusters(graph_t* g, graph_t* Xg, node_t* top, node_t* bot)
1012 {
1013 node_t *n;
1014 node_t *rep;
1015 edge_t *e;
1016 graph_t *sub;
1017
1018 if (is_a_cluster(g) && is_a_strong_cluster(g)) {
1019 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1020 if (agfstin(g, n) == 0) {
1021 rep = ND_rep(find(n));
1022 if (!top) top = makeXnode(Xg,TOPNODE);
1023 agedge(Xg, top, rep, 0, 1);
1024 }
1025 if (agfstout(g, n) == 0) {
1026 rep = ND_rep(find(n));
1027 if (!bot) bot = makeXnode(Xg,BOTNODE);
1028 agedge(Xg, rep, bot, 0, 1);
1029 }
1030 }
1031 if (top && bot) {
1032 e = agedge(Xg, top, bot, 0, 1);
1033 merge(e, 0, STRONG_CLUSTER_WEIGHT);
1034 }
1035 }
1036 for (sub = agfstsubg(g); sub; sub = agnxtsubg(sub))
1037 compile_clusters(sub, Xg, top, bot);
1038 }
1039
reverse_edge2(graph_t * g,edge_t * e)1040 static void reverse_edge2(graph_t * g, edge_t * e)
1041 {
1042 edge_t *rev;
1043
1044 rev = agfindedge(g, aghead(e), agtail(e));
1045 if (!rev)
1046 rev = agedge(g, aghead(e), agtail(e), 0, 1);
1047 merge(rev, ED_minlen(e), ED_weight(e));
1048 agdelete(g, e);
1049 }
1050
dfs(graph_t * g,node_t * v)1051 static void dfs(graph_t * g, node_t * v)
1052 {
1053 edge_t *e, *f;
1054 node_t *w;
1055
1056 if (ND_mark(v))
1057 return;
1058 ND_mark(v) = TRUE;
1059 ND_onstack(v) = TRUE;
1060 for (e = agfstout(g, v); e; e = f) {
1061 f = agnxtout(g, e);
1062 w = aghead(e);
1063 if (ND_onstack(w))
1064 reverse_edge2(g, e);
1065 else {
1066 if (ND_mark(w) == FALSE)
1067 dfs(g, w);
1068 }
1069 }
1070 ND_onstack(v) = FALSE;
1071 }
1072
break_cycles(graph_t * g)1073 static void break_cycles(graph_t * g)
1074 {
1075 node_t *n;
1076
1077 for (n = agfstnode(g); n; n = agnxtnode(g, n))
1078 ND_mark(n) = ND_onstack(n) = FALSE;
1079 for (n = agfstnode(g); n; n = agnxtnode(g, n))
1080 dfs(g, n);
1081 }
1082 /* setMinMax:
1083 * This will only be called with the root graph or a cluster
1084 * which are guaranteed to contain nodes. Thus, leader will be
1085 * set.
1086 */
setMinMax(graph_t * g,int doRoot)1087 static void setMinMax (graph_t* g, int doRoot)
1088 {
1089 int c, v;
1090 node_t *n;
1091 node_t* leader = NULL;
1092
1093 /* Do child clusters */
1094 for (c = 1; c <= GD_n_cluster(g); c++)
1095 setMinMax(GD_clust(g)[c], 0);
1096
1097 if (!GD_parent(g) && !doRoot) // root graph
1098 return;
1099
1100 GD_minrank(g) = MAXSHORT;
1101 GD_maxrank(g) = -1;
1102 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1103 v = ND_rank(n);
1104 if (GD_maxrank(g) < v)
1105 GD_maxrank(g) = v;
1106 if (GD_minrank(g) > v) {
1107 GD_minrank(g) = v;
1108 leader = n;
1109 }
1110 }
1111 GD_leader(g) = leader;
1112 }
1113
1114 /* readout_levels:
1115 * Store node rank information in original graph.
1116 * Set rank bounds in graph and clusters
1117 * Free added data structures.
1118 *
1119 * rank2 is called with balance=1, which ensures that minrank=0
1120 */
readout_levels(graph_t * g,graph_t * Xg,int ncc)1121 static void readout_levels(graph_t * g, graph_t * Xg, int ncc)
1122 {
1123 node_t *n;
1124 node_t *xn;
1125 int* minrk = NULL;
1126 int doRoot = 0;
1127
1128 GD_minrank(g) = MAXSHORT;
1129 GD_maxrank(g) = -1;
1130 if (ncc > 1) {
1131 int i;
1132 minrk = N_NEW(ncc+1,int);
1133 for (i = 1; i <= ncc; i++)
1134 minrk[i] = MAXSHORT;
1135 }
1136 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1137 xn = ND_rep(find(n));
1138 ND_rank(n) = ND_rank(xn);
1139 if (GD_maxrank(g) < ND_rank(n))
1140 GD_maxrank(g) = ND_rank(n);
1141 if (GD_minrank(g) > ND_rank(n))
1142 GD_minrank(g) = ND_rank(n);
1143 if (minrk) {
1144 ND_comp(n) = ND_comp(xn);
1145 minrk[ND_comp(n)] = MIN(minrk[ND_comp(n)],ND_rank(n));
1146 }
1147 }
1148 if (minrk) {
1149 for (n = agfstnode(g); n; n = agnxtnode(g, n))
1150 ND_rank(n) -= minrk[ND_comp(n)];
1151 /* Non-uniform shifting, so recompute maxrank/minrank of root graph */
1152 doRoot = 1;
1153 }
1154 else if (GD_minrank(g) > 0) { /* should never happen */
1155 int delta = GD_minrank(g);
1156 for (n = agfstnode(g); n; n = agnxtnode(g, n))
1157 ND_rank(n) -= delta;
1158 GD_minrank(g) -= delta;
1159 GD_maxrank(g) -= delta;
1160 }
1161
1162 setMinMax(g, doRoot);
1163
1164 /* release fastgraph memory from Xg */
1165 for (n = agfstnode(Xg); n; n = agnxtnode(Xg, n)) {
1166 free_list(ND_in(n));
1167 free_list(ND_out(n));
1168 }
1169
1170 free(ND_alg(agfstnode(g)));
1171 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1172 ND_alg(n) = NULL;
1173 }
1174 if (minrk)
1175 free (minrk);
1176 }
1177
dfscc(graph_t * g,node_t * n,int cc)1178 static void dfscc(graph_t * g, node_t * n, int cc)
1179 {
1180 edge_t *e;
1181 if (ND_comp(n) == 0) {
1182 ND_comp(n) = cc;
1183 for (e = agfstout(g, n); e; e = agnxtout(g, e))
1184 dfscc(g, aghead(e), cc);
1185 for (e = agfstin(g, n); e; e = agnxtin(g, e))
1186 dfscc(g, agtail(e), cc);
1187 }
1188 }
1189
connect_components(graph_t * g)1190 static int connect_components(graph_t * g)
1191 {
1192 int cc = 0;
1193 node_t *n;
1194
1195 for (n = agfstnode(g); n; n = agnxtnode(g, n))
1196 ND_comp(n) = 0;
1197 for (n = agfstnode(g); n; n = agnxtnode(g, n))
1198 if (ND_comp(n) == 0)
1199 dfscc(g, n, ++cc);
1200 if (cc > 1) {
1201 node_t *root = makeXnode(g, ROOT);
1202 int ncc = 1;
1203 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1204 if (ND_comp(n) == ncc) {
1205 (void) agedge(g, root, n, 0, 1);
1206 ncc++;
1207 }
1208 }
1209 }
1210 return (cc);
1211 }
1212
add_fast_edges(graph_t * g)1213 static void add_fast_edges (graph_t * g)
1214 {
1215 node_t *n;
1216 edge_t *e;
1217 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1218 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
1219 elist_append(e, ND_out(n));
1220 elist_append(e, ND_in(aghead(e)));
1221 }
1222 }
1223 }
1224
my_init_graph(Agraph_t * g,Agobj_t * graph,void * arg)1225 static void my_init_graph(Agraph_t *g, Agobj_t *graph, void *arg)
1226 { int *sz = arg; agbindrec(graph,"level graph rec",sz[0],TRUE); }
my_init_node(Agraph_t * g,Agobj_t * node,void * arg)1227 static void my_init_node(Agraph_t *g, Agobj_t *node, void *arg)
1228 { int *sz = arg; agbindrec(node,"level node rec",sz[1],TRUE); }
my_init_edge(Agraph_t * g,Agobj_t * edge,void * arg)1229 static void my_init_edge(Agraph_t *g, Agobj_t *edge, void *arg)
1230 { int *sz = arg; agbindrec(edge,"level edge rec",sz[2],TRUE); }
1231 static Agcbdisc_t mydisc = { {my_init_graph,0,0}, {my_init_node,0,0}, {my_init_edge,0,0} };
1232
1233 int infosizes[] = {
1234 sizeof(Agraphinfo_t),
1235 sizeof(Agnodeinfo_t),
1236 sizeof(Agedgeinfo_t)
1237 };
1238
dot2_rank(graph_t * g,aspect_t * asp)1239 void dot2_rank(graph_t * g, aspect_t* asp)
1240 {
1241 int ssize;
1242 int ncc, maxiter = INT_MAX;
1243 char *s;
1244 graph_t *Xg;
1245
1246 Last_node = NULL;
1247 Xg = agopen("level assignment constraints", Agstrictdirected, 0);
1248 agbindrec(Xg,"level graph rec",sizeof(Agraphinfo_t),TRUE);
1249 agpushdisc(Xg,&mydisc,infosizes);
1250
1251 edgelabel_ranks(g);
1252
1253 if ((s = agget(g, "nslimit1")))
1254 maxiter = atof(s) * agnnodes(g);
1255 else
1256 maxiter = INT_MAX;
1257
1258 compile_samerank(g, 0);
1259 compile_nodes(g, Xg);
1260 compile_edges(g, Xg);
1261 compile_clusters(g, Xg, 0, 0);
1262 break_cycles(Xg);
1263 ncc = connect_components(Xg);
1264 add_fast_edges (Xg);
1265
1266 if (asp) {
1267 init_UF_size(Xg);
1268 initEdgeTypes(Xg);
1269 }
1270
1271 if ((s = agget(g, "searchsize")))
1272 ssize = atoi(s);
1273 else
1274 ssize = -1;
1275 rank2(Xg, 1, maxiter, ssize);
1276 /* fastgr(Xg); */
1277 readout_levels(g, Xg, ncc);
1278 #ifdef DEBUG
1279 fprintf (stderr, "Xg %d nodes %d edges\n", agnnodes(Xg), agnedges(Xg));
1280 #endif
1281 agclose(Xg);
1282 }
1283