2
This software may only be used by you under license from AT&T Corp.
3
("AT&T"). A copy of AT&T's Source Code Agreement is available at
4
AT&T's Internet website having the URL:
5
<http://www.research.att.com/sw/tools/graphviz/license/source.html>
6
If you received this software without first entering into a license
7
with AT&T, you have an infringing copy of this software and cannot use
8
it without violating AT&T's intellectual property rights.
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.
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.
32
void dot_rank(graph_t* g)
36
/*collapse_leaves(g);*/
46
/* When there are edge labels, extra ranks are reserved here for the virtual
47
* nodes of the labels. This is done by doubling the input edge lengths.
48
* The input rank separation is adjusted to compensate.
50
void edgelabel_ranks(graph_t* g)
55
if (g->u.has_edge_labels) {
56
for (n = agfstnode(g); n; n = agnxtnode(g,n))
57
for (e = agfstout(g,n); e; e = agnxtout(g,e))
59
g->u.ranksep = (g->u.ranksep + 1) / 2;
63
/* Run the network simplex algorithm on each component. */
64
void rank1(graph_t* g)
70
if ((s = agget(g,"nslimit1")))
71
maxiter = atof(s) * agnnodes(g);
72
for (c = 0; c < g->u.comp.size; c++) {
73
g->u.nlist = g->u.comp.list[c];
78
int is_cluster(graph_t* g)
80
return (strncmp(g->name,"cluster",7) == 0);
83
int rank_set_class(graph_t* g)
85
static char *name[] = {"same","min","source","max","sink",NULL};
86
static int class[] = {SAMERANK,MINRANK,SOURCERANK,MAXRANK,SINKRANK,0};
89
if (is_cluster(g)) return CLUSTER;
90
val = maptoken(agget(g,"rank"),name,class);
95
/* Execute union commands for "same rank" subgraphs and clusters. */
96
void collapse_sets(graph_t* g)
103
mg = g->meta_node->graph;
104
for (me = agfstout(mg,g->meta_node); me; me = agnxtout(mg,me)) {
106
subg = agusergraph(mn);
108
c = rank_set_class(subg);
110
if ((c == CLUSTER) && CL_type == LOCAL) collapse_cluster(g,subg);
111
else collapse_rankset(g,subg,c);
114
/* mark nodes with ordered edges so their leaves are not collapsed */
115
if (agget(subg,"ordering"))
116
for (n = agfstnode(subg); n; n = agnxtnode(subg,n)) n->u.order = 1;
120
/* Merge the nodes of a min, max, or same rank set. */
121
void collapse_rankset(graph_t *g, graph_t *subg, int kind)
125
u = v = agfstnode(subg);
127
u->u.ranktype = kind;
128
while ((v = agnxtnode(subg,v))) {
130
v->u.ranktype = u->u.ranktype;
133
case MINRANK: case SOURCERANK:
134
if (g->u.minset == NULL) g->u.minset = u;
135
else g->u.minset = UF_union(g->u.minset,u);
137
case MAXRANK: case SINKRANK:
138
if (g->u.maxset == NULL) g->u.maxset = u;
139
else g->u.maxset = UF_union(g->u.maxset,u);
143
case SOURCERANK: g->u.minset->u.ranktype = kind; break;
144
case SINKRANK: g->u.maxset->u.ranktype = kind; break;
149
node_t* merge_leaves(graph_t *g, node_t *cur, node_t *new)
153
if (cur == NULL) rv = new;
155
rv = UF_union(cur,new);
156
rv->u.ht = MAX(cur->u.ht,new->u.ht);
157
rv->u.lw = cur->u.lw + new->u.lw + g->u.nodesep/2;
158
rv->u.rw = cur->u.rw + new->u.rw + g->u.nodesep/2;
163
void potential_leaf(graph_t* g, edge_t* e, node_t* leaf)
167
if ((e->u.tail_port.p.x) || (e->u.head_port.p.x)) return;
168
if ((e->u.minlen != 1) || (e->tail->u.order > 0)) return;
169
par = ((leaf != e->head)? e->head : e->tail);
170
leaf->u.ranktype = LEAFSET;
171
if (par == e->tail) par->u.outleaf = merge_leaves(g,par->u.outleaf,leaf);
172
else par->u.inleaf = merge_leaves(g,par->u.inleaf,leaf);
175
void collapse_leaves(graph_t* g)
180
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
182
/* consider n as a potential leaf of some other node. */
183
if ((n->u.ranktype != NOCMD) || (n->u.order)) continue;
184
if (agfstout(g,n) == NULL) {
185
if ((e = agfstin(g,n)) && (agnxtin(g,e) == NULL)) {
186
potential_leaf(g,e,n);
190
if (agfstin(g,n) == NULL) {
191
if ((e = agfstout(g,n)) && (agnxtout(g,e) == NULL)) {
192
potential_leaf(g,e,n);
199
/* To ensure that min and max rank nodes always have the intended rank
200
* assignment, reverse any incompatible edges.
202
void minmax_edges(graph_t* g)
208
srclen = sinklen = 0;
209
if ((g->u.maxset == NULL) && (g->u.minset == NULL)) return;
210
if ( g->u.minset != NULL ) g->u.minset = UF_find(g->u.minset);
211
if ( g->u.maxset != NULL ) g->u.maxset = UF_find(g->u.maxset);
213
if ((n = g->u.maxset)) {
214
sinklen = (g->u.maxset->u.ranktype == SINKRANK);
215
while ((e = n->u.out.list[0])) {
216
assert(e->head == UF_find(e->head));
220
if ((n = g->u.minset)) {
221
srclen = (g->u.minset->u.ranktype == SOURCERANK);
222
while ((e = n->u.in.list[0])) {
223
assert(e->tail == UF_find(e->tail));
228
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
229
if (n != UF_find(n)) continue;
230
if ((n->u.out.size == 0) && g->u.maxset && (n != g->u.maxset))
231
virtual_edge(n,g->u.maxset,NULL)->u.minlen = sinklen;
232
if ((n->u.in.size == 0) && g->u.minset && (n != g->u.minset))
233
virtual_edge(g->u.minset,n,NULL)->u.minlen = srclen;
238
* A cluster is collapsed in three steps.
239
* 1) The nodes of the cluster are ranked locally.
240
* 2) The cluster is collapsed into one node on the least rank.
241
* 3) In class1(), any inter-cluster edges are converted using
242
* the "virtual node + 2 edges" trick.
244
void collapse_cluster(graph_t *g, graph_t *subg)
247
if (agfstnode(subg) == NULL) return;
248
make_new_cluster(g,subg);
249
if (CL_type == LOCAL) {
251
cluster_leader(subg);
253
else scan_ranks(subg);
257
make_new_cluster(graph_t *g, graph_t *subg)
260
cno = ++(g->u.n_cluster);
261
g->u.clust = ZALLOC(cno+1,g->u.clust,graph_t*,g->u.n_cluster);
262
g->u.clust[cno] = subg;
263
do_graph_label(subg);
267
void node_induce(graph_t *par, graph_t *g)
273
/* enforce that a node is in at most one cluster at this level */
274
for (n = agfstnode(g); n; n = nn) {
276
if (n->u.ranktype) {agdelete(g,n); continue;}
277
for (i = 1; i < par->u.n_cluster; i++)
278
if (agcontains(par->u.clust[i],n)) break;
279
if (i < par->u.n_cluster) agdelete(g,n);
283
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
284
for (e = agfstout(g->root,n); e; e = agnxtout(g->root,e)) {
285
if (agcontains(g,e->head)) aginsert(g,e);
290
void cluster_leader(graph_t* clust)
295
/* find number of ranks and select a leader */
297
for (n = clust->u.nlist; n; n = n->u.next) {
298
if ((n->u.rank == 0) && (n->u.node_type == NORMAL)) leader = n;
299
if (maxrank < n->u.rank) maxrank = n->u.rank;
301
assert(leader != NULL);
302
clust->u.leader = leader;
304
for (n = agfstnode(clust); n; n = agnxtnode(clust,n)) {
305
assert ((n->u.UF_size <= 1) || (n == leader));
307
n->u.ranktype = CLUSTER;
312
* Assigns ranks of non-leader nodes.
313
* Expands same, min, max rank sets.
314
* Leaf sets and clusters remain merged.
315
* Sets minrank and maxrank appropriately.
317
void expand_ranksets(graph_t* g)
322
if ((n = agfstnode(g))) {
323
g->u.minrank = MAXSHORT;
327
/* The following works because n->u.rank == 0 if n is not in a
328
* cluster, and n->u.rank = the local rank offset if n is in
330
if (leader != n) n->u.rank += leader->u.rank;
332
if (g->u.maxrank < n->u.rank) g->u.maxrank = n->u.rank;
333
if (g->u.minrank > n->u.rank) g->u.minrank = n->u.rank;
335
if (n->u.ranktype && (n->u.ranktype != LEAFSET))
340
if (CL_type == LOCAL) {
341
for (c = 1; c <= g->u.n_cluster; c++)
342
set_minmax(g->u.clust[c]);
350
g->u.minrank = g->u.maxrank = 0;
354
void renewlist(elist* L)
357
for (i = L->size; i >= 0; i--) L->list[i] = NULL;
361
void cleanup1(graph_t* g)
367
for (c = 0; c < g->u.comp.size; c++) {
368
g->u.nlist = g->u.comp.list[c];
369
for (n = g->u.nlist; n; n = n->u.next) {
371
renewlist(&n->u.out);
375
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
376
for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
378
if (f && (e == f->u.to_orig)) free(f);
382
free(g->u.comp.list);
383
g->u.comp.list = NULL;
387
void set_minmax(graph_t* g)
391
g->u.minrank += g->u.leader->u.rank;
392
g->u.maxrank += g->u.leader->u.rank;
393
for (c = 1; c <= g->u.n_cluster; c++) set_minmax(g->u.clust[c]);
396
void scan_ranks(graph_t* g)
398
node_t *n,*leader=NULL;
399
g->u.minrank = MAXSHORT;
401
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
402
if (g->u.maxrank < n->u.rank) g->u.maxrank = n->u.rank;
403
if (g->u.minrank > n->u.rank) g->u.minrank = n->u.rank;
404
if (leader == NULL) leader = n;
405
else { if (n->u.rank < leader->u.rank) leader = n; }
407
g->u.leader = leader;
410
void find_clusters(graph_t* g)
416
mg = g->meta_node->graph;
417
for (me = agfstout(mg,g->meta_node); me; me = agnxtout(mg,me)) {
419
subg = agusergraph(mn);
421
if (subg->u.set_type == CLUSTER) collapse_cluster(g,subg);