/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/dotgen/rank.c

Go to the documentation of this file.
00001 /* $Id: rank.c,v 1.11 2007/09/28 14:38:43 north Exp $ $Revision: 1.11 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 
00018 /*
00019  * Rank the nodes of a directed graph, subject to user-defined
00020  * sets of nodes to be kept on the same, min, or max rank.
00021  * The temporary acyclic fast graph is constructed and ranked
00022  * by a network-simplex technique.  Then ranks are propagated
00023  * to non-leader nodes and temporary edges are deleted.
00024  * Leaf nodes and top-level clusters are left collapsed, though.
00025  * Assigns global minrank and maxrank of graph and all clusters.
00026  *
00027  * TODO: safety code.  must not be in two clusters at same level.
00028  *  must not be in same/min/max/rank and a cluster at the same time.
00029  *  watch out for interactions between leaves and clusters.
00030  */
00031 
00032 #include        "dot.h"
00033 
00034 static void 
00035 renewlist(elist * L)
00036 {
00037     int i;
00038     for (i = L->size; i >= 0; i--)
00039         L->list[i] = NULL;
00040     L->size = 0;
00041 }
00042 
00043 static void 
00044 cleanup1(graph_t * g)
00045 {
00046     node_t *n;
00047     edge_t *e, *f;
00048     int c;
00049 
00050     for (c = 0; c < GD_comp(g).size; c++) {
00051         GD_nlist(g) = GD_comp(g).list[c];
00052         for (n = GD_nlist(g); n; n = ND_next(n)) {
00053             renewlist(&ND_in(n));
00054             renewlist(&ND_out(n));
00055             ND_mark(n) = FALSE;
00056         }
00057     }
00058     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00059         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00060             f = ED_to_virt(e);
00061             if (f && (e == ED_to_orig(f))) {
00062                 /* Null out any other references to f to make sure we don't handle it
00063                  * a second time. For example, parallel multiedges share a virtual edge.
00064                  */
00065                 edge_t *e1, *f1;
00066                 for (e1 = agfstout(g, n); e1; e1 = agnxtout(g, e1)) {
00067                     if (e != e1) {
00068                         f1 = ED_to_virt(e1);
00069                         if (f1 && (f == f1)) {
00070                             ED_to_virt(e1) = NULL;
00071                         }
00072                     }
00073                 }
00074                 free(f);
00075             }
00076             ED_to_virt(e) = NULL;
00077         }
00078     }
00079     free(GD_comp(g).list);
00080     GD_comp(g).list = NULL;
00081     GD_comp(g).size = 0;
00082 }
00083 
00084 /* When there are edge labels, extra ranks are reserved here for the virtual
00085  * nodes of the labels.  This is done by doubling the input edge lengths.
00086  * The input rank separation is adjusted to compensate.
00087  */
00088 static void 
00089 edgelabel_ranks(graph_t * g)
00090 {
00091     node_t *n;
00092     edge_t *e;
00093 
00094     if (GD_has_labels(g) & EDGE_LABEL) {
00095         for (n = agfstnode(g); n; n = agnxtnode(g, n))
00096             for (e = agfstout(g, n); e; e = agnxtout(g, e))
00097                 ED_minlen(e) *= 2;
00098         GD_ranksep(g) = (GD_ranksep(g) + 1) / 2;
00099     }
00100 }
00101 
00102 /* Merge the nodes of a min, max, or same rank set. */
00103 static void 
00104 collapse_rankset(graph_t * g, graph_t * subg, int kind)
00105 {
00106     node_t *u, *v;
00107 
00108     u = v = agfstnode(subg);
00109     if (u) {
00110         ND_ranktype(u) = kind;
00111         while ((v = agnxtnode(subg, v))) {
00112             UF_union(u, v);
00113             ND_ranktype(v) = ND_ranktype(u);
00114         }
00115         switch (kind) {
00116         case MINRANK:
00117         case SOURCERANK:
00118             if (GD_minset(g) == NULL)
00119                 GD_minset(g) = u;
00120             else
00121                 GD_minset(g) = UF_union(GD_minset(g), u);
00122             break;
00123         case MAXRANK:
00124         case SINKRANK:
00125             if (GD_maxset(g) == NULL)
00126                 GD_maxset(g) = u;
00127             else
00128                 GD_maxset(g) = UF_union(GD_maxset(g), u);
00129             break;
00130         }
00131         switch (kind) {
00132         case SOURCERANK:
00133             GD_minset(g)->u.ranktype = kind;
00134             break;
00135         case SINKRANK:
00136             GD_maxset(g)->u.ranktype = kind;
00137             break;
00138         }
00139     }
00140 }
00141 
00142 static int 
00143 rank_set_class(graph_t * g)
00144 {
00145     static char *name[] = { "same", "min", "source", "max", "sink", NULL };
00146     static int class[] =
00147         { SAMERANK, MINRANK, SOURCERANK, MAXRANK, SINKRANK, 0 };
00148     int val;
00149 
00150     if (is_cluster(g))
00151         return CLUSTER;
00152     val = maptoken(agget(g, "rank"), name, class);
00153     GD_set_type(g) = val;
00154     return val;
00155 }
00156 
00157 static int 
00158 make_new_cluster(graph_t * g, graph_t * subg)
00159 {
00160     int cno;
00161     cno = ++(GD_n_cluster(g));
00162     GD_clust(g) = ZALLOC(cno + 1, GD_clust(g), graph_t *, GD_n_cluster(g));
00163     GD_clust(g)[cno] = subg;
00164     do_graph_label(subg);
00165     return cno;
00166 }
00167 
00168 static void 
00169 node_induce(graph_t * par, graph_t * g)
00170 {
00171     node_t *n, *nn;
00172     edge_t *e;
00173     int i;
00174 
00175     /* enforce that a node is in at most one cluster at this level */
00176     for (n = agfstnode(g); n; n = nn) {
00177         nn = agnxtnode(g, n);
00178         if (ND_ranktype(n)) {
00179             agdelete(g, n);
00180             continue;
00181         }
00182         for (i = 1; i < GD_n_cluster(par); i++)
00183             if (agcontains(GD_clust(par)[i], n))
00184                 break;
00185         if (i < GD_n_cluster(par))
00186             agdelete(g, n);
00187         ND_clust(n) = NULL;
00188     }
00189 
00190     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00191         for (e = agfstout(g->root, n); e; e = agnxtout(g->root, e)) {
00192             if (agcontains(g, e->head))
00193                 aginsert(g, e);
00194         }
00195     }
00196 }
00197 
00198 void 
00199 dot_scan_ranks(graph_t * g)
00200 {
00201     node_t *n, *leader = NULL;
00202     GD_minrank(g) = MAXSHORT;
00203     GD_maxrank(g) = -1;
00204     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00205         if (GD_maxrank(g) < ND_rank(n))
00206             GD_maxrank(g) = ND_rank(n);
00207         if (GD_minrank(g) > ND_rank(n))
00208             GD_minrank(g) = ND_rank(n);
00209         if (leader == NULL)
00210             leader = n;
00211         else {
00212             if (ND_rank(n) < ND_rank(leader))
00213                 leader = n;
00214         }
00215     }
00216     GD_leader(g) = leader;
00217 }
00218 
00219 static void
00220 cluster_leader(graph_t * clust)
00221 {
00222     node_t *leader, *n;
00223     int maxrank = 0;
00224 
00225     /* find number of ranks and select a leader */
00226     leader = NULL;
00227     for (n = GD_nlist(clust); n; n = ND_next(n)) {
00228         if ((ND_rank(n) == 0) && (ND_node_type(n) == NORMAL))
00229             leader = n;
00230         if (maxrank < ND_rank(n))
00231             maxrank = ND_rank(n);
00232     }
00233     assert(leader != NULL);
00234     GD_leader(clust) = leader;
00235 
00236     for (n = agfstnode(clust); n; n = agnxtnode(clust, n)) {
00237         assert((ND_UF_size(n) <= 1) || (n == leader));
00238         UF_union(n, leader);
00239         ND_ranktype(n) = CLUSTER;
00240     }
00241 }
00242 
00243 /*
00244  * A cluster is collapsed in three steps.
00245  * 1) The nodes of the cluster are ranked locally.
00246  * 2) The cluster is collapsed into one node on the least rank.
00247  * 3) In class1(), any inter-cluster edges are converted using
00248  *    the "virtual node + 2 edges" trick.
00249  */
00250 static void 
00251 collapse_cluster(graph_t * g, graph_t * subg)
00252 {
00253     if (GD_cluster_was_collapsed(subg))
00254         return;
00255     GD_cluster_was_collapsed(subg) = TRUE;
00256     node_induce(g, subg);
00257     if (agfstnode(subg) == NULL)
00258         return;
00259     make_new_cluster(g, subg);
00260     if (CL_type == LOCAL) {
00261         dot_rank(subg);
00262         cluster_leader(subg);
00263     } else
00264         dot_scan_ranks(subg);
00265 }
00266 
00267 /* Execute union commands for "same rank" subgraphs and clusters. */
00268 static void 
00269 collapse_sets(graph_t * g)
00270 {
00271     int c;
00272     graph_t *mg, *subg;
00273     node_t *mn, *n;
00274     edge_t *me;
00275 
00276     mg = g->meta_node->graph;
00277     for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) {
00278         mn = me->head;
00279         subg = agusergraph(mn);
00280 
00281         c = rank_set_class(subg);
00282         if (c) {
00283             if ((c == CLUSTER) && CL_type == LOCAL)
00284                 collapse_cluster(g, subg);
00285             else
00286                 collapse_rankset(g, subg, c);
00287         }
00288 
00289         /* mark nodes with ordered edges so their leaves are not collapsed */
00290         if (agget(subg, "ordering"))
00291             for (n = agfstnode(subg); n; n = agnxtnode(subg, n))
00292                 ND_order(n) = 1;
00293     }
00294 }
00295 
00296 static void 
00297 find_clusters(graph_t * g)
00298 {
00299     graph_t *mg, *subg;
00300     node_t *mn;
00301     edge_t *me;
00302 
00303     mg = g->meta_node->graph;
00304     for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) {
00305         mn = me->head;
00306         subg = agusergraph(mn);
00307 
00308         if (GD_set_type(subg) == CLUSTER)
00309             collapse_cluster(g, subg);
00310     }
00311 }
00312 
00313 static void 
00314 set_minmax(graph_t * g)
00315 {
00316     int c;
00317 
00318     GD_minrank(g) += GD_leader(g)->u.rank;
00319     GD_maxrank(g) += GD_leader(g)->u.rank;
00320     for (c = 1; c <= GD_n_cluster(g); c++)
00321         set_minmax(GD_clust(g)[c]);
00322 }
00323 
00324 /* To ensure that min and max rank nodes always have the intended rank
00325  * assignment, reverse any incompatible edges.
00326  */
00327 static point 
00328 minmax_edges(graph_t * g)
00329 {
00330     node_t *n;
00331     edge_t *e;
00332     point  slen;
00333 
00334     slen.x = slen.y = 0;
00335     if ((GD_maxset(g) == NULL) && (GD_minset(g) == NULL))
00336         return slen;
00337     if (GD_minset(g) != NULL)
00338         GD_minset(g) = UF_find(GD_minset(g));
00339     if (GD_maxset(g) != NULL)
00340         GD_maxset(g) = UF_find(GD_maxset(g));
00341 
00342     if ((n = GD_maxset(g))) {
00343         slen.y = (GD_maxset(g)->u.ranktype == SINKRANK);
00344         while ((e = ND_out(n).list[0])) {
00345             assert(e->head == UF_find(e->head));
00346             reverse_edge(e);
00347         }
00348     }
00349     if ((n = GD_minset(g))) {
00350         slen.x = (GD_minset(g)->u.ranktype == SOURCERANK);
00351         while ((e = ND_in(n).list[0])) {
00352             assert(e->tail == UF_find(e->tail));
00353             reverse_edge(e);
00354         }
00355     }
00356     return slen;
00357 }
00358     
00359 static int 
00360 minmax_edges2(graph_t * g, point slen)
00361 {
00362     node_t *n;
00363     edge_t *e = 0;
00364 
00365     if ((GD_maxset(g)) || (GD_minset(g))) {
00366         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00367             if (n != UF_find(n))
00368                 continue;
00369             if ((ND_out(n).size == 0) && GD_maxset(g) && (n != GD_maxset(g))) {
00370                 e = virtual_edge(n, GD_maxset(g), NULL);
00371                 ED_minlen(e) = slen.y;
00372             }
00373             if ((ND_in(n).size == 0) && GD_minset(g) && (n != GD_minset(g))) {
00374                 e = virtual_edge(GD_minset(g), n, NULL);
00375                 ED_minlen(e) = slen.x;
00376             }
00377         }
00378     }
00379     return (e != 0);
00380 }
00381 
00382 /* Run the network simplex algorithm on each component. */
00383 static void 
00384 rank1(graph_t * g)
00385 {
00386     int maxiter = INT_MAX;
00387     int c;
00388     char *s;
00389 
00390     if ((s = agget(g, "nslimit1")))
00391         maxiter = atof(s) * agnnodes(g);
00392     for (c = 0; c < GD_comp(g).size; c++) {
00393         GD_nlist(g) = GD_comp(g).list[c];
00394         rank(g, (GD_n_cluster(g) == 0 ? 1 : 0), maxiter);       /* TB balance */
00395     }
00396 }
00397 
00398 /* 
00399  * Assigns ranks of non-leader nodes.
00400  * Expands same, min, max rank sets.
00401  * Leaf sets and clusters remain merged.
00402  * Sets minrank and maxrank appropriately.
00403  */
00404 void expand_ranksets(graph_t * g)
00405 {
00406     int c;
00407     node_t *n, *leader;
00408 
00409     if ((n = agfstnode(g))) {
00410         GD_minrank(g) = MAXSHORT;
00411         GD_maxrank(g) = -1;
00412         while (n) {
00413             leader = UF_find(n);
00414             /* The following works because ND_rank(n) == 0 if n is not in a
00415              * cluster, and ND_rank(n) = the local rank offset if n is in
00416              * a cluster. */
00417             if (leader != n)
00418                 ND_rank(n) += ND_rank(leader);
00419 
00420             if (GD_maxrank(g) < ND_rank(n))
00421                 GD_maxrank(g) = ND_rank(n);
00422             if (GD_minrank(g) > ND_rank(n))
00423                 GD_minrank(g) = ND_rank(n);
00424 
00425             if (ND_ranktype(n) && (ND_ranktype(n) != LEAFSET))
00426                 UF_singleton(n);
00427             n = agnxtnode(g, n);
00428         }
00429         if (g == g->root) {
00430             if (CL_type == LOCAL) {
00431                 for (c = 1; c <= GD_n_cluster(g); c++)
00432                     set_minmax(GD_clust(g)[c]);
00433             } else {
00434                 find_clusters(g);
00435             }
00436         }
00437     } else {
00438         GD_minrank(g) = GD_maxrank(g) = 0;
00439     }
00440 }
00441 
00442 #ifdef ALLOW_LEVELS
00443 void
00444 setRanks (graph_t* g, attrsym_t* lsym)
00445 {
00446     node_t* n;
00447     char*   s;
00448     char*   ep;
00449     long    v;
00450 
00451     for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
00452         s = agxget (n, lsym->index);
00453         v = strtol (s, &ep, 10);
00454         if (ep == s)
00455             agerr(AGWARN, "no level attribute for node \"%s\"\n", n->name);
00456         ND_rank(n) = v;
00457     }
00458 }
00459 #endif
00460 
00461 void dot_rank(graph_t * g)
00462 {
00463     point p;
00464 #ifdef ALLOW_LEVELS
00465     attrsym_t* N_level;
00466 #endif
00467     edgelabel_ranks(g);
00468     collapse_sets(g);
00469     /*collapse_leaves(g); */
00470     class1(g);
00471     p = minmax_edges(g);
00472     decompose(g, 0);
00473     acyclic(g);
00474     if (minmax_edges2(g, p))
00475         decompose(g, 0);
00476 #ifdef ALLOW_LEVELS
00477     if ((N_level = agfindattr(g->proto->n, "level")))
00478         setRanks(g, N_level);
00479     else
00480 #endif
00481     rank1(g);
00482     expand_ranksets(g);
00483     cleanup1(g);
00484 }
00485 
00486 int is_cluster(graph_t * g)
00487 {
00488     return (strncmp(g->name, "cluster", 7) == 0);
00489 }
00490 
00491 #ifdef OBSOLETE
00492 static node_t*
00493 merge_leaves(graph_t * g, node_t * cur, node_t * new)
00494 {
00495     node_t *rv;
00496 
00497     if (cur == NULL)
00498         rv = new;
00499     else {
00500         rv = UF_union(cur, new);
00501         ND_ht_i(rv) = MAX(ND_ht_i(cur), ND_ht_i(new));
00502         ND_lw_i(rv) = ND_lw_i(cur) + ND_lw_i(new) + GD_nodesep(g) / 2;
00503         ND_rw_i(rv) = ND_rw_i(cur) + ND_rw_i(new) + GD_nodesep(g) / 2;
00504     }
00505     return rv;
00506 }
00507 
00508 static void 
00509 potential_leaf(graph_t * g, edge_t * e, node_t * leaf)
00510 {
00511     node_t *par;
00512 
00513     if ((ED_tail_port(e).p.x) || (ED_head_port(e).p.x))
00514         return;
00515     if ((ED_minlen(e) != 1) || (ND_order(e->tail) > 0))
00516         return;
00517     par = ((leaf != e->head) ? e->head : e->tail);
00518     ND_ranktype(leaf) = LEAFSET;
00519     if (par == e->tail)
00520         GD_outleaf(par) = merge_leaves(g, GD_outleaf(par), leaf);
00521     else
00522         GD_inleaf(par) = merge_leaves(g, GD_inleaf(par), leaf);
00523 }
00524 
00525 static void 
00526 collapse_leaves(graph_t * g)
00527 {
00528     node_t *n;
00529     edge_t *e;
00530 
00531     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00532 
00533         /* consider n as a potential leaf of some other node. */
00534         if ((ND_ranktype(n) != NOCMD) || (ND_order(n)))
00535             continue;
00536         if (agfstout(g, n) == NULL) {
00537             if ((e = agfstin(g, n)) && (agnxtin(g, e) == NULL)) {
00538                 potential_leaf(g, e, n);
00539                 continue;
00540             }
00541         }
00542         if (agfstin(g, n) == NULL) {
00543             if ((e = agfstout(g, n)) && (agnxtout(g, e) == NULL)) {
00544                 potential_leaf(g, e, n);
00545                 continue;
00546             }
00547         }
00548     }
00549 }
00550 #endif
00551 

Generated on Mon Mar 31 19:03:26 2008 for Graphviz by  doxygen 1.5.1