Graphviz  2.31.20130524.0447
lib/dotgen/cluster.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 #include "dot.h"
00016 
00017 static node_t*
00018 map_interclust_node(node_t * n)
00019 {
00020     node_t *rv;
00021 
00022 #ifndef WITH_CGRAPH
00023     if ((ND_clust(n) == NULL) || (ND_clust(n)->u.expanded))
00024 #else /* WITH_CGRAPH */
00025     if ((ND_clust(n) == NULL) || (  GD_expanded(ND_clust(n))) )
00026 #endif /* WITH_CGRAPH */
00027         rv = n;
00028     else
00029 #ifndef WITH_CGRAPH
00030         rv = ND_clust(n)->u.rankleader[ND_rank(n)];
00031 #else /* WITH_CGRAPH */
00032         rv = GD_rankleader(ND_clust(n))[ND_rank(n)];
00033 #endif /* WITH_CGRAPH */
00034     return rv;
00035 }
00036 
00037 /* make d slots starting at position pos (where 1 already exists) */
00038 static void 
00039 make_slots(graph_t * root, int r, int pos, int d)
00040 {
00041     int i;
00042     node_t *v, **vlist;
00043 #ifndef WITH_CGRAPH
00044     vlist = ND_rank(root)[r].v;
00045 #else /* WITH_CGRAPH */
00046     vlist = GD_rank(root)[r].v;
00047 #endif /* WITH_CGRAPH */
00048     if (d <= 0) {
00049 #ifndef WITH_CGRAPH
00050         for (i = pos - d + 1; i < ND_rank(root)[r].n; i++) {
00051 #else /* WITH_CGRAPH */
00052         for (i = pos - d + 1; i < GD_rank(root)[r].n; i++) {
00053 #endif /* WITH_CGRAPH */
00054             v = vlist[i];
00055             ND_order(v) = i + d - 1;
00056             vlist[ND_order(v)] = v;
00057         }
00058 #ifndef WITH_CGRAPH
00059         for (i = ND_rank(root)[r].n + d - 1; i < ND_rank(root)[r].n; i++)
00060 #else /* WITH_CGRAPH */
00061         for (i = GD_rank(root)[r].n + d - 1; i < GD_rank(root)[r].n; i++)
00062 #endif /* WITH_CGRAPH */
00063             vlist[i] = NULL;
00064     } else {
00065 /*assert(ND_rank(root)[r].n + d - 1 <= ND_rank(root)[r].an);*/
00066 #ifndef WITH_CGRAPH
00067         for (i = ND_rank(root)[r].n - 1; i > pos; i--) {
00068 #else /* WITH_CGRAPH */
00069         for (i = GD_rank(root)[r].n - 1; i > pos; i--) {
00070 #endif /* WITH_CGRAPH */
00071             v = vlist[i];
00072             ND_order(v) = i + d - 1;
00073             vlist[ND_order(v)] = v;
00074         }
00075         for (i = pos + 1; i < pos + d; i++)
00076             vlist[i] = NULL;
00077     }
00078 #ifndef WITH_CGRAPH
00079     ND_rank(root)[r].n += d - 1;
00080 #else /* WITH_CGRAPH */
00081     GD_rank(root)[r].n += d - 1;
00082 #endif /* WITH_CGRAPH */
00083 }
00084 
00085 static node_t* 
00086 clone_vn(graph_t * g, node_t * vn)
00087 {
00088     node_t *rv;
00089     int r;
00090 
00091     r = ND_rank(vn);
00092     make_slots(g, r, ND_order(vn), 2);
00093     rv = virtual_node(g);
00094     ND_lw(rv) = ND_lw(vn);
00095     ND_rw(rv) = ND_rw(vn);
00096     ND_rank(rv) = ND_rank(vn);
00097     ND_order(rv) = ND_order(vn) + 1;
00098     GD_rank(g)[r].v[ND_order(rv)] = rv;
00099     return rv;
00100 }
00101 
00102 static void 
00103 map_path(node_t * from, node_t * to, edge_t * orig, edge_t * ve, int type)
00104 {
00105     int r;
00106     node_t *u, *v;
00107     edge_t *e;
00108 
00109     assert(ND_rank(from) < ND_rank(to));
00110 
00111     if ((agtail(ve) == from) && (aghead(ve) == to))
00112         return;
00113 
00114     if (ED_count(ve) > 1) {
00115         ED_to_virt(orig) = NULL;
00116         if (ND_rank(to) - ND_rank(from) == 1) {
00117             if ((e = find_fast_edge(from, to)) && (ports_eq(orig, e))) {
00118                 merge_oneway(orig, e);
00119                 if ((ND_node_type(from) == NORMAL)
00120                     && (ND_node_type(to) == NORMAL))
00121                     other_edge(orig);
00122                 return;
00123             }
00124         }
00125         u = from;
00126         for (r = ND_rank(from); r < ND_rank(to); r++) {
00127             if (r < ND_rank(to) - 1)
00128                 v = clone_vn(agraphof(from), aghead(ve));
00129             else
00130                 v = to;
00131             e = virtual_edge(u, v, orig);
00132             ED_edge_type(e) = type;
00133             u = v;
00134             ED_count(ve)--;
00135             ve = ND_out(aghead(ve)).list[0];
00136         }
00137     } else {
00138         if (ND_rank(to) - ND_rank(from) == 1) {
00139             if ((ve = find_fast_edge(from, to)) && (ports_eq(orig, ve))) {
00140                 /*ED_to_orig(ve) = orig; */
00141                 ED_to_virt(orig) = ve;
00142                 ED_edge_type(ve) = type;
00143                 ED_count(ve)++;
00144                 if ((ND_node_type(from) == NORMAL)
00145                     && (ND_node_type(to) == NORMAL))
00146                     other_edge(orig);
00147             } else {
00148                 ED_to_virt(orig) = NULL;
00149                 ve = virtual_edge(from, to, orig);
00150                 ED_edge_type(ve) = type;
00151             }
00152         }
00153         if (ND_rank(to) - ND_rank(from) > 1) {
00154             e = ve;
00155             if (agtail(ve) != from) {
00156                 ED_to_virt(orig) = NULL;
00157                 e = ED_to_virt(orig) = virtual_edge(from, aghead(ve), orig);
00158                 delete_fast_edge(ve);
00159             } else
00160                 e = ve;
00161             while (ND_rank(aghead(e)) != ND_rank(to))
00162                 e = ND_out(aghead(e)).list[0];
00163             if (aghead(e) != to) {
00164                 ve = e;
00165                 e = virtual_edge(agtail(e), to, orig);
00166                 ED_edge_type(e) = type;
00167                 delete_fast_edge(ve);
00168             }
00169         }
00170     }
00171 }
00172 
00173 static void 
00174 make_interclust_chain(graph_t * g, node_t * from, node_t * to, edge_t * orig)
00175 {
00176     int newtype;
00177     node_t *u, *v;
00178 
00179     u = map_interclust_node(from);
00180     v = map_interclust_node(to);
00181     if ((u == from) && (v == to))
00182         newtype = VIRTUAL;
00183     else
00184         newtype = CLUSTER_EDGE;
00185     map_path(u, v, orig, ED_to_virt(orig), newtype);
00186 }
00187 
00188 /* 
00189  * attach and install edges between clusters.
00190  * essentially, class2() for interclust edges.
00191  */
00192 void interclexp(graph_t * subg)
00193 {
00194     graph_t *g;
00195     node_t *n;
00196     edge_t *e, *prev, *next;
00197 
00198     g = agroot(subg);
00199     for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
00200 
00201         /* N.B. n may be in a sub-cluster of subg */
00202         prev = NULL;
00203         for (e = agfstedge(agroot(subg), n); e; e = next) {
00204             next = agnxtedge(agroot(subg), e, n);
00205             if (agcontains(subg, e))
00206                 continue;
00207 
00208 #ifdef WITH_CGRAPH
00209                 /* canonicalize edge */
00210             e = AGMKOUT(e);
00211 #endif
00212             /* short/flat multi edges */
00213             if (mergeable(prev, e)) {
00214                 if (ND_rank(agtail(e)) == ND_rank(aghead(e)))
00215                     ED_to_virt(e) = prev;
00216                 else
00217                     ED_to_virt(e) = NULL;
00218                 if (ED_to_virt(prev) == NULL)
00219                     continue;   /* internal edge */
00220                 merge_chain(subg, e, ED_to_virt(prev), FALSE);
00221                 safe_other_edge(e);
00222                 continue;
00223             }
00224 
00225             /* flat edges */
00226             if (ND_rank(agtail(e)) == ND_rank(aghead(e))) {
00227                 edge_t* fe;
00228                 if ((fe = find_flat_edge(agtail(e), aghead(e))) == NULL) {
00229                     flat_edge(g, e);
00230                     prev = e;
00231                 } else if (e != fe) {
00232                     safe_other_edge(e);
00233                     if (!ED_to_virt(e)) merge_oneway(e, fe);
00234                 }
00235                 continue;
00236             }
00237 
00238 /* This assertion is still valid if the new ranking is not used */
00239 #ifndef WITH_CGRAPH
00240             assert(ED_to_virt(e) != NULL);
00241 #endif
00242 
00243             /* forward edges */
00244             if (ND_rank(aghead(e)) > ND_rank(agtail(e))) {
00245                 make_interclust_chain(g, agtail(e), aghead(e), e);
00246                 prev = e;
00247                 continue;
00248             }
00249 
00250             /* backward edges */
00251             else {
00252 /*
00253 I think that make_interclust_chain should create call other_edge(e) anyway 
00254                                 if (agcontains(subg,agtail(e))
00255                                         && agfindedge(subg->root,aghead(e),agtail(e))) other_edge(e);
00256 */
00257                 make_interclust_chain(g, aghead(e), agtail(e), e);
00258                 prev = e;
00259             }
00260         }
00261     }
00262 }
00263 
00264 static void 
00265 merge_ranks(graph_t * subg)
00266 {
00267     int i, d, r, pos, ipos;
00268     node_t *v;
00269     graph_t *root;
00270 
00271     root = agroot(subg);
00272     if (GD_minrank(subg) > 0)
00273 #ifndef WITH_CGRAPH
00274         ND_rank(root)[GD_minrank(subg) - 1].valid = FALSE;
00275 #else /* WITH_CGRAPH */
00276         GD_rank(root)[GD_minrank(subg) - 1].valid = FALSE;
00277 #endif /* WITH_CGRAPH */
00278     for (r = GD_minrank(subg); r <= GD_maxrank(subg); r++) {
00279         d = GD_rank(subg)[r].n;
00280 #ifndef WITH_CGRAPH
00281         ipos = pos = GD_rankleader(subg)[r]->u.order;
00282 #else /* WITH_CGRAPH */
00283         ipos = pos = ND_order(GD_rankleader(subg)[r]);
00284 #endif /* WITH_CGRAPH */
00285         make_slots(root, r, pos, d);
00286         for (i = 0; i < GD_rank(subg)[r].n; i++) {
00287 #ifndef WITH_CGRAPH
00288             v = ND_rank(root)[r].v[pos] = GD_rank(subg)[r].v[i];
00289 #else /* WITH_CGRAPH */
00290             v = GD_rank(root)[r].v[pos] = GD_rank(subg)[r].v[i];
00291 #endif /* WITH_CGRAPH */
00292             ND_order(v) = pos++;
00293 #ifndef WITH_CGRAPH
00294             v->graph = subg->root;
00295 #else /* WITH_CGRAPH */
00296         /* real nodes automatically have v->root = root graph */
00297             if (ND_node_type(v) == VIRTUAL)
00298                 v->root = root;
00299 #endif /* WITH_CGRAPH */
00300             delete_fast_node(subg, v);
00301             fast_node(agroot(subg), v);
00302             GD_n_nodes(agroot(subg))++;
00303         }
00304 #ifndef WITH_CGRAPH
00305         GD_rank(subg)[r].v = ND_rank(root)[r].v + ipos;
00306         ND_rank(root)[r].valid = FALSE;
00307 #else /* WITH_CGRAPH */
00308         GD_rank(subg)[r].v = GD_rank(root)[r].v + ipos;
00309         GD_rank(root)[r].valid = FALSE;
00310 #endif /* WITH_CGRAPH */
00311     }
00312     if (r < GD_maxrank(root))
00313         GD_rank(root)[r].valid = FALSE;
00314     GD_expanded(subg) = TRUE;
00315 }
00316 
00317 static void 
00318 remove_rankleaders(graph_t * g)
00319 {
00320     int r;
00321     node_t *v;
00322     edge_t *e;
00323 
00324     for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
00325         v = GD_rankleader(g)[r];
00326 
00327         /* remove the entire chain */
00328         while ((e = ND_out(v).list[0]))
00329             delete_fast_edge(e);
00330         while ((e = ND_in(v).list[0]))
00331             delete_fast_edge(e);
00332         delete_fast_node(agroot(g), v);
00333         GD_rankleader(g)[r] = NULL;
00334     }
00335 }
00336 
00337 /* delete virtual nodes of a cluster, and install real nodes or sub-clusters */
00338 void expand_cluster(graph_t * subg)
00339 {
00340     /* build internal structure of the cluster */
00341     class2(subg);
00342     GD_comp(subg).size = 1;
00343     GD_comp(subg).list[0] = GD_nlist(subg);
00344     allocate_ranks(subg);
00345     build_ranks(subg, 0);
00346     merge_ranks(subg);
00347 
00348     /* build external structure of the cluster */
00349     interclexp(subg);
00350     remove_rankleaders(subg);
00351 }
00352 
00353 /* this function marks every node in <g> with its top-level cluster under <g> */
00354 void mark_clusters(graph_t * g)
00355 {
00356     int c;
00357     node_t *n, *nn, *vn;
00358     edge_t *orig, *e;
00359     graph_t *clust;
00360 
00361     /* remove sub-clusters below this level */
00362     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00363         if (ND_ranktype(n) == CLUSTER)
00364             UF_singleton(n);
00365         ND_clust(n) = NULL;
00366     }
00367 
00368     for (c = 1; c <= GD_n_cluster(g); c++) {
00369         clust = GD_clust(g)[c];
00370         for (n = agfstnode(clust); n; n = nn) {
00371                 nn = agnxtnode(clust,n);
00372             if (ND_ranktype(n) != NORMAL) {
00373                 agerr(AGWARN,
00374                       "%s was already in a rankset, deleted from cluster %s\n",
00375                       agnameof(n), agnameof(g));
00376                 agdelete(clust,n);
00377                 continue;
00378             }
00379             UF_setname(n, GD_leader(clust));
00380             ND_clust(n) = clust;
00381             ND_ranktype(n) = CLUSTER;
00382 
00383             /* here we mark the vnodes of edges in the cluster */
00384             for (orig = agfstout(clust, n); orig;
00385                  orig = agnxtout(clust, orig)) {
00386                 if ((e = ED_to_virt(orig))) {
00387 #ifndef WITH_CGRAPH
00388                     while (e && (vn = e->head)->u.node_type == VIRTUAL) {
00389 #else /* WITH_CGRAPH */
00390                     while (e && ND_node_type(vn =aghead(e)) == VIRTUAL) {
00391 #endif /* WITH_CGRAPH */
00392                         ND_clust(vn) = clust;
00393                         e = ND_out(aghead(e)).list[0];
00394                         /* trouble if concentrators and clusters are mixed */
00395                     }
00396                 }
00397             }
00398         }
00399     }
00400 }
00401 
00402 void build_skeleton(graph_t * g, graph_t * subg)
00403 {
00404     int r;
00405     node_t *v, *prev, *rl;
00406     edge_t *e;
00407 
00408     prev = NULL;
00409     GD_rankleader(subg) = N_NEW(GD_maxrank(subg) + 2, node_t *);
00410     for (r = GD_minrank(subg); r <= GD_maxrank(subg); r++) {
00411         v = GD_rankleader(subg)[r] = virtual_node(g);
00412         ND_rank(v) = r;
00413         ND_ranktype(v) = CLUSTER;
00414         ND_clust(v) = subg;
00415         if (prev) {
00416             e = virtual_edge(prev, v, NULL);
00417             ED_xpenalty(e) *= CL_CROSS;
00418         }
00419         prev = v;
00420     }
00421 
00422     /* set the counts on virtual edges of the cluster skeleton */
00423     for (v = agfstnode(subg); v; v = agnxtnode(subg, v)) {
00424         rl = GD_rankleader(subg)[ND_rank(v)];
00425         ND_UF_size(rl)++;
00426         for (e = agfstout(subg, v); e; e = agnxtout(subg, e)) {
00427             for (r = ND_rank(agtail(e)); r < ND_rank(aghead(e)); r++) {
00428                 ED_count(ND_out(rl).list[0])++;
00429             }
00430         }
00431     }
00432     for (r = GD_minrank(subg); r <= GD_maxrank(subg); r++) {
00433         rl = GD_rankleader(subg)[r];
00434         if (ND_UF_size(rl) > 1)
00435             ND_UF_size(rl)--;
00436     }
00437 }
00438 
00439 void install_cluster(graph_t * g, node_t * n, int pass, nodequeue * q)
00440 {
00441     int r;
00442     graph_t *clust;
00443 
00444     clust = ND_clust(n);
00445     if (GD_installed(clust) != pass + 1) {
00446         for (r = GD_minrank(clust); r <= GD_maxrank(clust); r++)
00447             install_in_rank(g, GD_rankleader(clust)[r]);
00448         for (r = GD_minrank(clust); r <= GD_maxrank(clust); r++)
00449             enqueue_neighbors(q, GD_rankleader(clust)[r], pass);
00450         GD_installed(clust) = pass + 1;
00451     }
00452 }
00453 
00454 static void mark_lowcluster_basic(Agraph_t * g);
00455 void mark_lowclusters(Agraph_t * root)
00456 {
00457     Agnode_t *n, *vn;
00458     Agedge_t *orig, *e;
00459 
00460     /* first, zap any previous cluster labelings */
00461     for (n = agfstnode(root); n; n = agnxtnode(root, n)) {
00462         ND_clust(n) = NULL;
00463         for (orig = agfstout(root, n); orig; orig = agnxtout(root, orig)) {
00464             if ((e = ED_to_virt(orig))) {
00465 #ifndef WITH_CGRAPH
00466                 while (e && (vn = e->head)->u.node_type == VIRTUAL) {
00467 #else /* WITH_CGRAPH */
00468                 while (e && (ND_node_type(vn = aghead(e))) == VIRTUAL) {
00469 #endif /* WITH_CGRAPH */
00470                     ND_clust(vn) = NULL;
00471                     e = ND_out(aghead(e)).list[0];
00472                 }
00473             }
00474         }
00475     }
00476 
00477     /* do the recursion */
00478     mark_lowcluster_basic(root);
00479 }
00480 
00481 static void mark_lowcluster_basic(Agraph_t * g)
00482 {
00483     Agraph_t *clust;
00484     Agnode_t *n, *vn;
00485     Agedge_t *orig, *e;
00486     int c;
00487 
00488     for (c = 1; c <= GD_n_cluster(g); c++) {
00489         clust = GD_clust(g)[c];
00490         mark_lowcluster_basic(clust);
00491     }
00492     /* see what belongs to this graph that wasn't already marked */
00493     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00494         if (ND_clust(n) == NULL)
00495             ND_clust(n) = g;
00496         for (orig = agfstout(g, n); orig; orig = agnxtout(g, orig)) {
00497             if ((e = ED_to_virt(orig))) {
00498 #ifndef WITH_CGRAPH
00499                 while (e && (vn = e->head)->u.node_type == VIRTUAL) {
00500 #else /* WITH_CGRAPH */
00501                 while (e && (ND_node_type(vn = aghead(e))) == VIRTUAL) {
00502 #endif /* WITH_CGRAPH */
00503                     if (ND_clust(vn) == NULL)
00504                         ND_clust(vn) = g;
00505                     e = ND_out(aghead(e)).list[0];
00506                 }
00507             }
00508         }
00509     }
00510 }