|
Graphviz
2.29.20120523.0446
|
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 #include "dot.h" 00015 00016 /* 00017 * Author: Mohammad T. Irfan 00018 * Summer, 2008 00019 */ 00020 00021 /* TODO: 00022 * - Support clusters 00023 * - Support disconnected graphs 00024 * - Provide algorithms for aspect ratios < 1 00025 */ 00026 00027 #define DPI 72 00028 00029 /* 00030 * NODE GROUPS FOR SAME RANKING 00031 * Node group data structure groups nodes together for 00032 * MIN, MAX, SOURCE, SINK constraints. 00033 * The grouping is based on the union-find data structure and 00034 * provides sequential access to the nodes in the same group. 00035 */ 00036 00037 /* data structure for node groups */ 00038 typedef struct nodeGroup_t { 00039 node_t **nodes; 00040 int nNodes; 00041 double width, height; 00042 } nodeGroup_t; 00043 00044 static nodeGroup_t *nodeGroups; 00045 static int nNodeGroups = 0; 00046 00047 /* computeNodeGroups: 00048 * computeNodeGroups function does the groupings of nodes. 00049 * The grouping is based on the union-find data structure. 00050 */ 00051 static void computeNodeGroups(graph_t * g) 00052 { 00053 node_t *n; 00054 00055 nodeGroups = N_GNEW(agnnodes(g), nodeGroup_t); 00056 00057 nNodeGroups = 0; 00058 00059 /* initialize node ids. Id of a node is used as an index to the group. */ 00060 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00061 ND_id(n) = -1; 00062 } 00063 00064 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00065 if (ND_UF_size(n) == 0) { /* no same ranking constraint */ 00066 nodeGroups[nNodeGroups].nodes = NEW(node_t *); 00067 nodeGroups[nNodeGroups].nodes[0] = n; 00068 nodeGroups[nNodeGroups].nNodes = 1; 00069 nodeGroups[nNodeGroups].width = ND_width(n); 00070 nodeGroups[nNodeGroups].height = ND_height(n); 00071 00072 ND_id(n) = nNodeGroups; 00073 nNodeGroups++; 00074 } else /* group same ranked nodes */ 00075 { 00076 node_t *l = UF_find(n); 00077 if (ND_id(l) > -1) /* leader is already grouped */ 00078 { 00079 int index = ND_id(l); 00080 nodeGroups[index].nodes[nodeGroups[index].nNodes++] = n; 00081 nodeGroups[index].width += ND_width(n); 00082 nodeGroups[index].height = 00083 (nodeGroups[index].height < 00084 ND_height(n)) ? ND_height(n) : nodeGroups[index]. 00085 height; 00086 00087 ND_id(n) = index; 00088 } else /* create a new group */ 00089 { 00090 nodeGroups[nNodeGroups].nodes = 00091 N_NEW(ND_UF_size(l), node_t *); 00092 00093 if (l == n) /* node n is the leader */ 00094 { 00095 nodeGroups[nNodeGroups].nodes[0] = l; 00096 nodeGroups[nNodeGroups].nNodes = 1; 00097 nodeGroups[nNodeGroups].width = ND_width(l); 00098 nodeGroups[nNodeGroups].height = ND_height(l); 00099 } else { 00100 nodeGroups[nNodeGroups].nodes[0] = l; 00101 nodeGroups[nNodeGroups].nodes[1] = n; 00102 nodeGroups[nNodeGroups].nNodes = 2; 00103 nodeGroups[nNodeGroups].width = 00104 ND_width(l) + ND_width(n); 00105 nodeGroups[nNodeGroups].height = 00106 (ND_height(l) < 00107 ND_height(n)) ? ND_height(n) : ND_height(l); 00108 } 00109 00110 ND_id(l) = nNodeGroups; 00111 ND_id(n) = nNodeGroups; 00112 nNodeGroups++; 00113 } 00114 } 00115 } 00116 00117 } 00118 00119 /* 00120 * END OF CODES FOR NODE GROUPS 00121 */ 00122 00123 /* countDummyNodes: 00124 * Count the number of dummy nodes 00125 */ 00126 int countDummyNodes(graph_t * g) 00127 { 00128 int count = 0; 00129 node_t *n; 00130 edge_t *e; 00131 00132 /* Count dummy nodes */ 00133 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00134 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00135 #ifdef UNUSED 00136 /* this loop can be avoided */ 00137 for (k = ND_rank(agtail(e))+1; k < ND_rank(aghead(e)); k++) { 00138 count++; 00139 } 00140 #endif 00141 /* flat edges do not have dummy nodes */ 00142 if (ND_rank(aghead(e)) != ND_rank(agtail(e))) 00143 count += abs(ND_rank(aghead(e)) - ND_rank(agtail(e))) - 1; 00144 } 00145 } 00146 return count; 00147 } 00148 00149 /* 00150 * FFDH PACKING ALGORITHM TO ACHIEVE TARGET ASPECT RATIO 00151 */ 00152 00153 /* 00154 * layerWidthInfo_t: data structure for keeping layer width information 00155 * Each layer consists of a number of node groups. 00156 */ 00157 typedef struct layerWidthInfo_t { 00158 int layerNumber; 00159 nodeGroup_t **nodeGroupsInLayer; 00160 int *removed; /* is the node group removed? */ 00161 int nNodeGroupsInLayer; 00162 int nDummyNodes; 00163 double width; 00164 double height; 00165 } layerWidthInfo_t; 00166 00167 static layerWidthInfo_t *layerWidthInfo = NULL; 00168 static int *sortedLayerIndex; 00169 static int nLayers = 0; 00170 00171 /* computeLayerWidths: 00172 */ 00173 static void computeLayerWidths(graph_t * g) 00174 { 00175 int i; 00176 node_t *v; 00177 node_t *n; 00178 edge_t *e; 00179 00180 nLayers = 0; 00181 00182 /* free previously allocated memory */ 00183 if (layerWidthInfo) { 00184 for (i = 0; i < nNodeGroups; i++) { 00185 if (layerWidthInfo[i].nodeGroupsInLayer) { 00186 int j; 00187 for (j = 0; j < layerWidthInfo[i].nNodeGroupsInLayer; j++) { 00188 //if (layerWidthInfo[i].nodeGroupsInLayer[j]) 00189 //free(layerWidthInfo[i].nodeGroupsInLayer[j]); 00190 } 00191 free(layerWidthInfo[i].nodeGroupsInLayer); 00192 } 00193 if (layerWidthInfo[i].removed) 00194 free(layerWidthInfo[i].removed); 00195 } 00196 00197 free(layerWidthInfo); 00198 } 00199 /* allocate memory 00200 * the number of layers can go up to the number of node groups 00201 */ 00202 layerWidthInfo = N_NEW(nNodeGroups, layerWidthInfo_t); 00203 00204 for (i = 0; i < nNodeGroups; i++) { 00205 layerWidthInfo[i].nodeGroupsInLayer = 00206 N_NEW(nNodeGroups, nodeGroup_t *); 00207 00208 layerWidthInfo[i].removed = N_NEW(nNodeGroups, int); 00209 00210 layerWidthInfo[i].layerNumber = i; 00211 layerWidthInfo[i].nNodeGroupsInLayer = 0; 00212 layerWidthInfo[i].nDummyNodes = 0; 00213 layerWidthInfo[i].width = 0.0; 00214 layerWidthInfo[i].height = 0.0; 00215 } 00216 00217 00218 00219 /* Count dummy nodes in the layer */ 00220 for (n = agfstnode(g); n; n = agnxtnode(g, n)) 00221 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00222 int k; 00223 00224 /* FIX: This loop maybe unnecessary, but removing it and using 00225 * the commented codes next, gives a segmentation fault. I 00226 * forgot the reason why. 00227 */ 00228 for (k = ND_rank(agtail(e)) + 1; k < ND_rank(aghead(e)); k++) { 00229 layerWidthInfo[k].nDummyNodes++; 00230 } 00231 00232 #ifdef UNUSED 00233 if (ND_rank(aghead(e)) != ND_rank(agtail(e))) 00234 /* flat edges do not have dummy nodes */ 00235 layerWidthInfo[k].nDummyNodes = abs(ND_rank(aghead(e)) - ND_rank(agtail(e))) - 1; 00236 #endif 00237 } 00238 00239 #ifdef UNUSED 00240 /***************************************************************** 00241 * This code is removed. It considers dummy nodes in layer width, 00242 * which does not give good results in experiments. 00243 *****************************************************************/ 00244 00245 for (i = 0; i < nNodeGroups; i++) { 00246 v = nodeGroups[i].nodes[0]; 00247 layerWidthInfo[ND_rank(v)].width = (layerWidthInfo[ND_rank(v)].nDummyNodes - 1) * GD_nodesep(g); 00248 } 00249 #endif 00250 00251 /* gather the layer information */ 00252 for (i = 0; i < nNodeGroups; i++) { 00253 v = nodeGroups[i].nodes[0]; 00254 if (ND_rank(v) + 1 > nLayers) /* update the number of layers */ 00255 nLayers = ND_rank(v) + 1; 00256 00257 layerWidthInfo[ND_rank(v)].width += 00258 nodeGroups[i].width * DPI + (layerWidthInfo[ND_rank(v)].width > 00259 0) * GD_nodesep(g); 00260 if (layerWidthInfo[ND_rank(v)].height < nodeGroups[i].height * DPI) 00261 layerWidthInfo[ND_rank(v)].height = nodeGroups[i].height * DPI; 00262 layerWidthInfo[ND_rank(v)]. 00263 nodeGroupsInLayer[layerWidthInfo[ND_rank(v)]. 00264 nNodeGroupsInLayer] = &nodeGroups[i]; 00265 layerWidthInfo[ND_rank(v)].nNodeGroupsInLayer++; 00266 } 00267 00268 } 00269 00270 /* compFunction: 00271 * Comparison function to be used in qsort. 00272 * For sorting the layers by nonincreasing width 00273 */ 00274 static int compFunction(const void *a, const void *b) 00275 { 00276 int *ind1 = (int *) a; 00277 int *ind2 = (int *) b; 00278 00279 return (layerWidthInfo[*ind2].width > 00280 layerWidthInfo[*ind1].width) - (layerWidthInfo[*ind2].width < 00281 layerWidthInfo[*ind1].width); 00282 } 00283 00284 /* sortLayers: 00285 * Sort the layers by width (nonincreasing order) 00286 * qsort should be replaced by insertion sort for better performance. 00287 * (layers are "almost" sorted during iterations) 00288 */ 00289 static void sortLayers(graph_t * g) 00290 { 00291 qsort(sortedLayerIndex, agnnodes(g), sizeof(int), compFunction); 00292 } 00293 00294 #ifdef UNUSED 00295 /* getMaxDummyNodes: 00296 * get the max # of dummy nodes on the incoming edges to a nodeGroup 00297 */ 00298 static int getMaxDummyNodes(nodeGroup_t * ng) 00299 { 00300 int i, max = 0, cnt = 0; 00301 for (i = 0; i < ng->nNodes; i++) { 00302 node_t *n = ng->nodes[i]; 00303 edge_t *e; 00304 graph_t *g = agraphof(n); 00305 for (e = agfstin(g, n); e; e = agnxtin(g, e)) { 00306 cnt += ND_rank(aghead(e)) - ND_rank(agtail(e)); // it's 1 more than the original count 00307 if (ND_rank(aghead(e)) - ND_rank(agtail(e)) > max) 00308 max = ND_rank(aghead(e)) - ND_rank(agtail(e)); 00309 } 00310 } 00311 00312 return max; 00313 } 00314 #endif 00315 00316 /* getOutDegree: 00317 * Return the sum of out degrees of the nodes in a node group. 00318 */ 00319 static int getOutDegree(nodeGroup_t * ng) 00320 { 00321 int i, cnt = 0; 00322 for (i = 0; i < ng->nNodes; i++) { 00323 node_t *n = ng->nodes[i]; 00324 edge_t *e; 00325 graph_t *g = agraphof(n); 00326 00327 /* count outdegree. This loop might be unnecessary. */ 00328 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00329 cnt++; 00330 } 00331 } 00332 00333 return cnt; 00334 } 00335 00336 /* compFunction2: 00337 * Comparison function to be used in qsort. 00338 * For sorting the node groups by their out degrees (nondecreasing) 00339 */ 00340 static int compFunction2(const void *a, const void *b) 00341 { 00342 nodeGroup_t **ind1 = (nodeGroup_t **) a, **ind2 = (nodeGroup_t **) b; 00343 00344 int cnt1 = getOutDegree(*ind1); 00345 int cnt2 = getOutDegree(*ind2); 00346 00347 return (cnt2 < cnt1) - (cnt2 > cnt1); 00348 } 00349 00350 #ifdef UNUSED 00351 /* compFunction3: 00352 * Comparison function to be used in qsort. 00353 * For sorting the node groups by their height & width 00354 */ 00355 static int compFunction3(const void *a, const void *b) 00356 { 00357 nodeGroup_t **ind1 = (nodeGroup_t **) a, **ind2 = (nodeGroup_t **) b; 00358 if ((*ind2)->height == (*ind1)->height) 00359 return ((*ind2)->width < (*ind1)->width) - ((*ind2)->width > 00360 (*ind1)->width); 00361 00362 return ((*ind2)->height < (*ind1)->height) - ((*ind2)->height > 00363 (*ind1)->height); 00364 } 00365 00366 /***************************************************************** 00367 * The following commented codes are no longer used 00368 * Originally used in the cocktail tool. 00369 *****************************************************************/ 00370 /* checkLayerConstraints: 00371 * check if there is any node group in the layer 00372 * that is not constrained by MIN/MAX/SOURCE/SINK-RANK constraints. 00373 */ 00374 static int checkLayerConstraints(layerWidthInfo_t lwi) 00375 { 00376 int i; 00377 for (i = 0; i < lwi.nNodeGroupsInLayer; i++) { 00378 if (lwi.nodeGroupsInLayer[i]->nNodes > 0) { 00379 int rtype = ND_ranktype(lwi.nodeGroupsInLayer[i]->nodes[0]); 00380 if (rtype != MINRANK && rtype != MAXRANK && rtype != SOURCERANK 00381 && rtype != SINKRANK) 00382 return 1; 00383 } 00384 } 00385 00386 return 0; 00387 } 00388 00389 /* checkLayerConstraints: 00390 * check if all the node groups in the layer are 00391 * constrained by MIN/MAX/SOURCE/SINK-RANK constraints 00392 */ 00393 static int checkLayerConstraints(layerWidthInfo_t lwi) 00394 { 00395 int i; 00396 for (i = 0; i < lwi.nNodeGroupsInLayer; i++) { 00397 if (lwi.nodeGroupsInLayer[i]->nNodes > 0) { 00398 int rtype = ND_ranktype(lwi.nodeGroupsInLayer[i]->nodes[0]); 00399 if (rtype != MINRANK && rtype != MAXRANK && rtype != SOURCERANK 00400 && rtype != SINKRANK) 00401 return 0; 00402 } 00403 } 00404 00405 return 1; 00406 } 00407 00408 /* checkNodeGroupConstraints: 00409 * check if the node group is not constrained by 00410 * MIN/MAX/SOURCE/SINK-RANK constraints 00411 * Only used in the cocktail tool. 00412 */ 00413 static int checkNodeGroupConstraints(nodeGroup_t * ndg) 00414 { 00415 int i; 00416 int rtype = ND_ranktype(ndg->nodes[0]); 00417 00418 if (rtype != MINRANK && rtype != MAXRANK && rtype != SOURCERANK 00419 && rtype != SINKRANK) 00420 return 1; 00421 00422 return 0; 00423 } 00424 00425 /* checkHorizontalEdge: 00426 * check if there is an edge from ng to a node in 00427 * layerWidthInfo[nextLayerIndex]. 00428 * Only used in the cocktail tool. 00429 */ 00430 static int 00431 checkHorizontalEdge(graph_t * g, nodeGroup_t * ng, int nextLayerIndex) 00432 { 00433 int i; 00434 edge_t *e; 00435 00436 for (i = 0; i < ng->nNodes; i++) { 00437 for (e = agfstout(g, ng->nodes[i]); e; e = agnxtout(g, e)) { 00438 if (layerWidthInfo[nextLayerIndex].layerNumber == 00439 ND_rank(aghead(e))) { 00440 return 1; 00441 } 00442 } 00443 } 00444 00445 00446 return 0; 00447 } 00448 00449 /* hasMaxOrSinkNodes: 00450 * check if the the layer lwi has MAX or SINK nodes 00451 * Only used in the cocktail tool. 00452 */ 00453 static int hasMaxOrSinkNodes(layerWidthInfo_t * lwi) 00454 { 00455 int i, j; 00456 00457 for (i = 0; i < lwi->nNodeGroupsInLayer; i++) { 00458 if (lwi->removed[i]) 00459 continue; 00460 for (j = 0; j < lwi->nodeGroupsInLayer[i]->nNodes; j++) { 00461 if (ND_ranktype(lwi->nodeGroupsInLayer[i]->nodes[j]) == MAXRANK 00462 || ND_ranktype(lwi->nodeGroupsInLayer[i]->nodes[j]) == 00463 SINKRANK) 00464 return 1; 00465 } 00466 } 00467 00468 return 0; 00469 } 00470 00471 /* reduceMaxWidth: 00472 * The following function is no longer used. 00473 * Originally used for FFDH packing heuristic 00474 * FFDH procedure 00475 */ 00476 static void reduceMaxWidth(graph_t * g) 00477 { 00478 int i; 00479 int maxLayerIndex; // = sortedLayerIndex[0]; 00480 double nextMaxWidth; // = (nLayers > 1) ? layerWidthInfo[sortedLayerIndex[1]].width : 0; 00481 double w = 0; 00482 Agnode_t *v; 00483 00484 for (i = 0; i < nLayers; i++) { 00485 if (layerWidthInfo[sortedLayerIndex[i]].nNodeGroupsInLayer <= 1) // || !checkLayerConstraints(layerWidthInfo[sortedLayerIndex[i]])) 00486 continue; 00487 else { 00488 maxLayerIndex = sortedLayerIndex[i]; 00489 nextMaxWidth = 00490 (nLayers > 00491 i + 1) ? layerWidthInfo[sortedLayerIndex[i + 00492 1]].width : 0; 00493 break; 00494 } 00495 } 00496 00497 if (i == nLayers) 00498 return; //reduction of layerwidth is not possible. 00499 00500 00501 //sort the node groups in maxLayerIndex layer by height and then width, nonincreasing 00502 qsort(layerWidthInfo[maxLayerIndex].nodeGroupsInLayer, 00503 layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer, 00504 sizeof(nodeGroup_t *), compFunction2); 00505 //printf("ht0 = %lf, ht1 = %lf\n", ND_height(layerWidthInfo[maxLayerIndex].nodes[0]), ND_height(layerWidthInfo[maxLayerIndex].nodes[1])); 00506 00507 00508 if (nextMaxWidth <= layerWidthInfo[maxLayerIndex].width / 2 00509 || nextMaxWidth == layerWidthInfo[maxLayerIndex].width) 00510 nextMaxWidth = layerWidthInfo[maxLayerIndex].width / 2; 00511 00512 double targetWidth = nextMaxWidth; //layerWidthInfo[maxLayerIndex].width/2; 00513 00514 //printf("max = %lf, target = %lf\n", layerWidthInfo[maxLayerIndex].width, targetWidth);//, w + (layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i])->width ); 00515 //getchar(); 00516 00517 00518 //packing by node demotion 00519 int nextLayerIndex = -1; 00520 for (i = 0; i < nLayers; i++) { 00521 if (layerWidthInfo[i].layerNumber == 00522 layerWidthInfo[maxLayerIndex].layerNumber + 1) 00523 nextLayerIndex = i; 00524 } 00525 00526 if (nextLayerIndex > -1) { 00527 //if (layerWidthInfo[nextLayerIndex].width <= 0.5*layerWidthInfo[maxLayerIndex].width) 00528 //{ 00529 int changed = 0; 00530 //demote nodes to the next layer 00531 for (i = 0; i < layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer; 00532 i++) { 00533 if (layerWidthInfo[maxLayerIndex].removed[i]) 00534 continue; 00535 00536 if (!checkHorizontalEdge 00537 (g, layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i], 00538 nextLayerIndex) 00539 && w + layerWidthInfo[nextLayerIndex].width + 00540 (layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i])-> 00541 width <= targetWidth) { 00542 w += (layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i])-> 00543 width; 00544 changed++; 00545 00546 int j; 00547 nodeGroup_t *ng = 00548 layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i]; 00549 00550 layerWidthInfo[maxLayerIndex].removed[i] = 1; 00551 layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer--; 00552 layerWidthInfo[maxLayerIndex].width -= ng->width; 00553 for (j = 0; j < ng->nNodes; j++) 00554 ND_rank(ng->nodes[j])++; 00555 00556 00557 layerWidthInfo[nextLayerIndex]. 00558 nodeGroupsInLayer[layerWidthInfo[nextLayerIndex]. 00559 nNodeGroupsInLayer] = ng; 00560 layerWidthInfo[nextLayerIndex]. 00561 removed[layerWidthInfo[nextLayerIndex]. 00562 nNodeGroupsInLayer] = 0; 00563 layerWidthInfo[nextLayerIndex].nNodeGroupsInLayer++; 00564 layerWidthInfo[nextLayerIndex].width += ng->width; 00565 00566 //int jj; 00567 //for (jj = 0; jj < layerWidthInfo[nextLayerIndex].nNodeGroupsInLayer; jj++) { 00568 //Agnode_t *node = layerWidthInfo[nextLayerIndex].nodeGroupsInLayer[jj]->nodes[0]; 00569 //printf("%s\n", agnameof(node)); 00570 //} 00571 } 00572 00573 } 00574 00575 if (changed) { 00576 //printf("Demoted %d nodes\n", changed); 00577 return; 00578 } 00579 //} 00580 } 00581 //packing by creating new layers. Must be commented out if packing by demotion is used 00582 00583 //going to create a new layer. increase the rank of all higher ranked nodes. (to be modified...) 00584 for (v = agfstnode(g); v; v = agnxtnode(g, v)) { 00585 if (ND_rank(v) > layerWidthInfo[maxLayerIndex].layerNumber) 00586 ND_rank(v)++; 00587 } 00588 00589 for (i = 0; i < nLayers; i++) { 00590 if (layerWidthInfo[i].layerNumber > 00591 layerWidthInfo[maxLayerIndex].layerNumber) 00592 layerWidthInfo[i].layerNumber++; 00593 } 00594 00595 //now partition the current layer into two layers (to be modified to support general case of > 2 layers) 00596 int flag = 0; //is a new layer created? 00597 int alt = 0; 00598 for (i = 0; i < layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer; i++) { 00599 if (layerWidthInfo[maxLayerIndex].removed[i]) 00600 continue; 00601 00602 //nodesep-> only if there are > 1 nodes******************************* 00603 if ((w + 00604 (layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i])->width * 00605 DPI + (w > 0) * GD_nodesep(g) <= targetWidth && alt == 0 00606 && ND_ranktype(layerWidthInfo[maxLayerIndex]. 00607 nodeGroupsInLayer[i]->nodes[0]) != SINKRANK 00608 && ND_ranktype(layerWidthInfo[maxLayerIndex]. 00609 nodeGroupsInLayer[i]->nodes[0]) != MAXRANK) 00610 || 00611 (ND_ranktype 00612 (layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i]-> 00613 nodes[0]) != SINKRANK 00614 && ND_ranktype(layerWidthInfo[maxLayerIndex]. 00615 nodeGroupsInLayer[i]->nodes[0]) != MAXRANK 00616 && hasMaxOrSinkNodes(&layerWidthInfo[maxLayerIndex])) 00617 ) 00618 //&& ND_pinned(layerWidthInfo[maxLayerIndex].nodes[i]) == 0 ) 00619 { 00620 w += (layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i])-> 00621 width * DPI + (w > 0) * GD_nodesep(g); 00622 alt = 1; 00623 } else { 00624 int j; 00625 nodeGroup_t *ng = 00626 layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i]; 00627 00628 flag = 1; 00629 00630 layerWidthInfo[maxLayerIndex].removed[i] = 1; 00631 layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer--; 00632 layerWidthInfo[maxLayerIndex].nDummyNodes++; 00633 layerWidthInfo[maxLayerIndex].width -= 00634 (ng->width * DPI + GD_nodesep(g)); 00635 for (j = 0; j < ng->nNodes; j++) 00636 ND_rank(ng->nodes[j])++; 00637 00638 //create new layer 00639 layerWidthInfo[nLayers]. 00640 nodeGroupsInLayer[layerWidthInfo[nLayers]. 00641 nNodeGroupsInLayer] = ng; 00642 layerWidthInfo[nLayers].nNodeGroupsInLayer++; 00643 layerWidthInfo[nLayers].layerNumber = ND_rank(ng->nodes[0]); 00644 00645 layerWidthInfo[nLayers].width += (ng->width * DPI + (layerWidthInfo[nLayers].nNodeGroupsInLayer > 1) * GD_nodesep(g)); // just add the node widths now. 00646 00647 alt = 0; 00648 } 00649 } 00650 00651 if (flag) { 00652 //calculate number of dummy nodes 00653 node_t *n; 00654 edge_t *e; 00655 int nDummy = 0; 00656 00657 for (n = agfstnode(g); n; n = agnxtnode(g, n)) 00658 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00659 if ((ND_rank(aghead(e)) > layerWidthInfo[nLayers].layerNumber 00660 && ND_rank(agtail(e)) < 00661 layerWidthInfo[nLayers].layerNumber) 00662 || (ND_rank(aghead(e)) < 00663 layerWidthInfo[nLayers].layerNumber 00664 && ND_rank(agtail(e)) > 00665 layerWidthInfo[nLayers].layerNumber) 00666 ) 00667 nDummy++; 00668 } 00669 00670 layerWidthInfo[nLayers].nDummyNodes = nDummy; 00671 layerWidthInfo[nLayers].width += 00672 (layerWidthInfo[nLayers].nDummyNodes - 1) * GD_nodesep(g); 00673 nLayers++; 00674 } 00675 00676 else { 00677 //undo increment of ranks and layerNumbers.***************** 00678 for (v = agfstnode(g); v; v = agnxtnode(g, v)) { 00679 if (ND_rank(v) > layerWidthInfo[maxLayerIndex].layerNumber + 1) 00680 ND_rank(v)--; 00681 } 00682 00683 for (i = 0; i < nLayers; i++) { 00684 if (layerWidthInfo[i].layerNumber > 00685 layerWidthInfo[maxLayerIndex].layerNumber + 1) 00686 layerWidthInfo[i].layerNumber--; 00687 } 00688 } 00689 } 00690 #endif 00691 00692 /* reduceMaxWidth2: 00693 * This is the main heuristic for partitioning the widest layer. 00694 * Partitioning is based on outdegrees of nodes. 00695 * Replace compFunction2 by compFunction3 if you want to partition 00696 * by node widths and heights. 00697 * FFDH procedure 00698 */ 00699 static void reduceMaxWidth2(graph_t * g) 00700 { 00701 int i; 00702 int maxLayerIndex; 00703 double nextMaxWidth; 00704 double w = 0; 00705 double targetWidth; 00706 int fst; 00707 nodeGroup_t *fstNdGrp; 00708 int ndem; 00709 int p, q; 00710 int limit; 00711 int rem; 00712 int rem2; 00713 00714 00715 /* Find the widest layer. it must have at least 2 nodes. */ 00716 for (i = 0; i < nLayers; i++) { 00717 if (layerWidthInfo[sortedLayerIndex[i]].nNodeGroupsInLayer <= 1) 00718 continue; 00719 else { 00720 maxLayerIndex = sortedLayerIndex[i]; 00721 /* get the width of the next widest layer */ 00722 nextMaxWidth = 00723 (nLayers > 00724 i + 1) ? layerWidthInfo[sortedLayerIndex[i + 00725 1]].width : 0; 00726 break; 00727 } 00728 } 00729 00730 if (i == nLayers) 00731 return; /* reduction of layerwidth is not possible. */ 00732 00733 /* sort the node groups in maxLayerIndex layer by height and 00734 * then width, nonincreasing 00735 */ 00736 qsort(layerWidthInfo[maxLayerIndex].nodeGroupsInLayer, 00737 layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer, 00738 sizeof(nodeGroup_t *), compFunction2); 00739 00740 #if 0 00741 printf("\nSorted nodes in mx layer:\n---------------------------\n"); 00742 for (i = 0; i < layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer; i++) { 00743 Agnode_t *node = layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i]->nodes[0]; 00744 printf("%s. width=%lf, height=%lf\n", 00745 agnameof(node), node->width, node->height); 00746 } 00747 #endif 00748 00749 if (nextMaxWidth <= layerWidthInfo[maxLayerIndex].width / 4 00750 || nextMaxWidth >= layerWidthInfo[maxLayerIndex].width * 3 / 4) 00751 nextMaxWidth = layerWidthInfo[maxLayerIndex].width / 2; 00752 00753 targetWidth = nextMaxWidth; /* layerWidthInfo[maxLayerIndex].width/2; */ 00754 00755 /* now partition the current layer into two or more 00756 * layers (determined by the ranking algorithm) 00757 */ 00758 fst = 0; 00759 ndem = 0; 00760 limit = layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer; 00761 rem = 0; 00762 rem2 = 0; 00763 00764 /* initialize w, the width of the widest layer after partitioning */ 00765 w = 0; 00766 00767 for (i = 0; i < limit + rem; i++) { 00768 if (layerWidthInfo[maxLayerIndex].removed[i]) { 00769 rem++; 00770 continue; 00771 } 00772 00773 if ((w + 00774 layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i]->width * 00775 DPI + (w > 0) * GD_nodesep(g) <= targetWidth) 00776 || !fst) { 00777 w += (layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i])-> 00778 width * DPI + (w > 0) * GD_nodesep(g); 00779 if (!fst) { 00780 fstNdGrp = 00781 layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i]; 00782 fst = 1; 00783 } 00784 } else { 00785 nodeGroup_t *ng = 00786 layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i]; 00787 00788 00789 #ifdef UNUSED 00790 /* The following code corrects w by adding dummy node spacing. 00791 * It's no longer used 00792 */ 00793 int l; 00794 for (l = 0; l < ng->nNodes; l++) { 00795 w += (ND_in(ng->nodes[l]).size - 1) * GD_nodesep(g); 00796 } 00797 #endif 00798 00799 for (p = 0; p < fstNdGrp->nNodes; p++) 00800 for (q = 0; q < ng->nNodes; q++) { 00801 //printf("Trying to add virtual edge: %s -> %s\n", 00802 // agnameof(fstNdGrp->nodes[p]), agnameof(ng->nodes[q])); 00803 00804 /* The following code is for deletion of long virtual edges. 00805 * It's no longer used. 00806 */ 00807 #ifdef UNUSED 00808 for (s = ND_in(ng->nodes[q]).size - 1; s >= 0; s--) { 00809 ev = ND_in(ng->nodes[q]).list[s]; 00810 00811 edge_t *et; 00812 int fail = 0; 00813 cnt = 0; 00814 00815 for (et = agfstin(g, aghead(ev)); et; 00816 et = agnxtin(g, et)) { 00817 if (aghead(et) == aghead(ev) 00818 && agtail(et) == agtail(ev)) { 00819 fail = 1; 00820 break; 00821 } 00822 } 00823 00824 if (fail) { 00825 //printf("FAIL DETECTED\n"); 00826 continue; 00827 } 00828 00829 00830 if (ED_edge_type(ev) == VIRTUAL 00831 && ND_rank(aghead(ev)) > ND_rank(agtail(ev)) + 1) { 00832 //printf("%d. inNode= %s.deleted: %s->%s\n", 00833 // test++, agnameof(ng->nodes[q]), 00834 // agnameof(agtail(ev)), agnameof(aghead(ev))); 00835 00836 delete_fast_edge(ev); 00837 free(ev); 00838 } 00839 } 00840 #endif 00841 00842 /* add a new virtual edge */ 00843 edge_t *newVEdge = 00844 virtual_edge(fstNdGrp->nodes[p], ng->nodes[q], 00845 NULL); 00846 ED_edge_type(newVEdge) = VIRTUAL; 00847 ndem++; /* increase number of node demotions */ 00848 } 00849 00850 /* the following code updates the layer width information. The 00851 * update is not useful in the current version of the heuristic. 00852 */ 00853 layerWidthInfo[maxLayerIndex].removed[i] = 1; 00854 rem2++; 00855 layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer--; 00856 /* SHOULD BE INCREASED BY THE SUM OF INDEG OF ALL NODES IN GROUP */ 00857 layerWidthInfo[maxLayerIndex].nDummyNodes++; 00858 layerWidthInfo[maxLayerIndex].width -= 00859 (ng->width * DPI + GD_nodesep(g)); 00860 } 00861 } 00862 } 00863 00864 #ifdef UNUSED 00865 /* balanceLayers: 00866 * The following is the layer balancing heuristic. 00867 * Balance the widths of the layers as much as possible. 00868 * It's no longer used. 00869 */ 00870 static void balanceLayers(graph_t * g) 00871 { 00872 int maxLayerIndex, nextLayerIndex, i; 00873 double maxWidth, w; 00874 00875 //get the max width layer number 00876 00877 for (i = 0; i < nLayers; i++) { 00878 if (layerWidthInfo[sortedLayerIndex[i]].nNodeGroupsInLayer <= 1 00879 || 00880 layerWidthInfo[sortedLayerIndex[i]].layerNumber + 1 == nLayers) 00881 continue; 00882 else { 00883 maxLayerIndex = sortedLayerIndex[i]; 00884 maxWidth = layerWidthInfo[maxLayerIndex].width; 00885 printf("Balancing: maxLayerIndex = %d\n", maxLayerIndex); 00886 break; 00887 } 00888 } 00889 00890 if (i == nLayers) 00891 return; //reduction of layerwidth is not possible. 00892 00893 //balancing ~~ packing by node demotion 00894 nextLayerIndex = -1; 00895 for (i = 0; i < nLayers; i++) { 00896 if (layerWidthInfo[i].layerNumber == 00897 layerWidthInfo[maxLayerIndex].layerNumber + 1) { 00898 nextLayerIndex = i; 00899 } 00900 } 00901 00902 if (nextLayerIndex > -1) { 00903 //if (layerWidthInfo[nextLayerIndex].width <= 0.5*layerWidthInfo[maxLayerIndex].width) 00904 //{ 00905 int changed = 0; 00906 w = 0; 00907 00908 //demote nodes to the next layer 00909 for (i = 0; i < layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer; 00910 i++) { 00911 if (layerWidthInfo[maxLayerIndex].removed[i]) 00912 continue; 00913 00914 if (!checkHorizontalEdge 00915 (g, layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i], 00916 nextLayerIndex) 00917 && layerWidthInfo[nextLayerIndex].width 00918 /*+ (layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i])->width */ 00919 <= layerWidthInfo[maxLayerIndex].width 00920 /*- (layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i])->width*/ 00921 ) { 00922 w += (layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i])-> 00923 width; 00924 changed++; 00925 00926 int j; 00927 nodeGroup_t *ng = 00928 layerWidthInfo[maxLayerIndex].nodeGroupsInLayer[i]; 00929 00930 layerWidthInfo[maxLayerIndex].removed[i] = 1; 00931 layerWidthInfo[maxLayerIndex].nNodeGroupsInLayer--; 00932 layerWidthInfo[maxLayerIndex].width -= (ng->width); 00933 layerWidthInfo[maxLayerIndex].nDummyNodes++; 00934 for (j = 0; j < ng->nNodes; j++) 00935 ND_rank(ng->nodes[j])++; 00936 00937 00938 layerWidthInfo[nextLayerIndex]. 00939 nodeGroupsInLayer[layerWidthInfo[nextLayerIndex]. 00940 nNodeGroupsInLayer] = ng; 00941 layerWidthInfo[nextLayerIndex]. 00942 removed[layerWidthInfo[nextLayerIndex]. 00943 nNodeGroupsInLayer] = 0; 00944 layerWidthInfo[nextLayerIndex].nNodeGroupsInLayer++; 00945 layerWidthInfo[nextLayerIndex].width += 00946 (ng->width + GD_nodesep(g)); 00947 } 00948 00949 } 00950 00951 if (changed) { 00952 //printf("Demoted %d nodes\n", changed); 00953 return; 00954 } 00955 //} 00956 } 00957 } 00958 00959 /* applyPacking: 00960 * The following is the initial packing heuristic 00961 * It's no longer used. 00962 */ 00963 static void applyPacking(graph_t * g, double targetAR) 00964 { 00965 int i; 00966 00967 sortedLayerIndex = N_NEW(agnnodes(g), int); 00968 00969 for (i = 0; i < agnnodes(g); i++) { 00970 sortedLayerIndex[i] = i; 00971 } 00972 00973 00974 node_t *v; 00975 00976 for (v = agfstnode(g); v; v = agnxtnode(g, v)) { 00977 //printf("%s, rank = %d, ranktype = %d\n", agnameof(v), ND_rank(v), ND_ranktype(v)); 00978 } 00979 00980 //GD_nodesep(g) = 0.25; 00981 //GD_ranksep(g) = 0.25; 00983 //printf("Nodesep = %d, Ranksep = %d\n",GD_nodesep(g), GD_ranksep(g)); 00984 00985 00986 for (i = 0; i < 1; i++) { 00987 //printf("iteration = %d\n----------------------\n", i); 00988 for (v = agfstnode(g); v; v = agnxtnode(g, v)) { 00989 //printf("%s rank = %d\n", agnameof(v), ND_rank(v)); 00990 } 00991 00992 computeLayerWidths(g); 00993 sortLayers(g); 00994 reduceMaxWidth(g); 00995 00996 //printf("====================\n"); 00997 } 00998 00999 01000 int k; 01001 01002 for (k = 0; k < nLayers - 1; k++) { 01003 int cnt = 0, tg; 01004 if (layerWidthInfo[k].nNodeGroupsInLayer > 7) { 01005 01006 cnt = 0; 01007 tg = layerWidthInfo[k].nNodeGroupsInLayer - 7; 01008 01009 for (i = layerWidthInfo[k].nNodeGroupsInLayer - 1; i >= 0; i--) { 01010 01011 if (layerWidthInfo[k].removed[i]) 01012 continue; 01013 01014 int j; 01015 nodeGroup_t *ng = layerWidthInfo[k].nodeGroupsInLayer[i]; 01016 01017 01018 layerWidthInfo[k].removed[i] = 1; 01019 layerWidthInfo[k].nNodeGroupsInLayer--; 01020 layerWidthInfo[k].nDummyNodes++; 01021 layerWidthInfo[k].width -= 01022 (ng->width * DPI + GD_nodesep(g)); 01023 for (j = 0; j < ng->nNodes; j++) 01024 ND_rank(ng->nodes[j])++; 01025 01026 //create new layer 01027 layerWidthInfo[k + 01028 1].nodeGroupsInLayer[layerWidthInfo[k + 01029 1]. 01030 nNodeGroupsInLayer] = 01031 ng; 01032 layerWidthInfo[k + 1].nNodeGroupsInLayer++; 01033 //layerWidthInfo[k+1].layerNumber = ND_rank(ng->nodes[0]); 01034 01035 //layerWidthInfo[k+1].width += ( ng->width*DPI + (layerWidthInfo[nLayers].nNodeGroupsInLayer > 1) * GD_nodesep(g) ); // just add the node widths now. 01036 01037 cnt++; 01038 01039 if (cnt == tg) 01040 break; 01041 01042 } 01043 } 01044 } 01045 01046 //calcualte the max width 01047 int maxW = 0; 01048 int nNodeG = 0, l, nDummy = 0; 01049 int index; 01050 01051 for (k = 0; k < nLayers; k++) { 01052 //printf("Layer#=%d, #dumNd=%d, width=%0.1lf, node=%s\n", layerWidthInfo[k].layerNumber, layerWidthInfo[k].nDummyNodes, layerWidthInfo[k].width, 01053 // agnameof(layerWidthInfo[k].nodeGroupsInLayer[0]->nodes[0])); 01054 if (layerWidthInfo[k].width > maxW) // && layerWidthInfo[k].nNodeGroupsInLayer > 0) 01055 { 01056 maxW = layerWidthInfo[k].width; 01057 nNodeG = layerWidthInfo[k].nNodeGroupsInLayer; 01058 l = layerWidthInfo[k].layerNumber; 01059 nDummy = layerWidthInfo[k].nDummyNodes; 01060 index = k; 01061 } 01062 } 01063 //printf("Ht=%d, MxW=%d, #ndGr=%d, #dumNd=%d, lyr#=%d, 1stNd=%s\n", (nLayers-1)*DPI, maxW, nNodeG, nDummy, l, agnameof(layerWidthInfo[index].nodeGroupsInLayer[0]->nodes[0])); 01064 01065 // printf("Finally...\n------------------\n"); 01066 for (v = agfstnode(g); v; v = agnxtnode(g, v)) { 01067 //printf("%s, rank = %d, ranktype = %d\n", agnameof(v, ND_rank(v), ND_ranktype(v)); 01068 } 01069 01070 } 01071 #endif 01072 01073 /* applyPacking2: 01074 * The following is the packing heuristic for wide layout. 01075 */ 01076 static void applyPacking2(graph_t * g) 01077 { 01078 int i; 01079 01080 sortedLayerIndex = N_NEW(agnnodes(g), int); 01081 01082 for (i = 0; i < agnnodes(g); i++) { 01083 sortedLayerIndex[i] = i; 01084 } 01085 01086 computeLayerWidths(g); 01087 sortLayers(g); 01088 reduceMaxWidth2(g); 01089 01090 } 01091 01092 #ifdef UNUSED 01093 /* applyPacking4: 01094 * The following is the packing heuristic for wide layout. 01095 * It's used with Nikolov-Healy healy heuristic. 01096 */ 01097 void applyPacking4(graph_t * g) 01098 { 01099 int i; 01100 01101 sortedLayerIndex = N_NEW(agnnodes(g), int); 01102 01103 for (i = 0; i < agnnodes(g); i++) { 01104 sortedLayerIndex[i] = i; 01105 } 01106 01107 01108 for (i = 0; i < 1; i++) { 01109 /* printf("iteration = %d\n----------------------\n", i); 01110 for (v = agfstnode(g); v; v = agnxtnode(g,v)) 01111 { 01112 printf("%s rank = %d\n", agnameof(v), ND_rank(v)); 01113 } 01114 */ 01115 01116 01117 computeLayerWidths(g); 01118 sortLayers(g); 01119 reduceMaxWidth2(g); 01120 //printf("====================\n"); 01121 } 01122 } 01123 01124 /* 01125 * NOCOLOV & HEALY'S NODE PROMOTION HEURISTIC 01126 */ 01127 01128 /**************************************************************** 01129 * This data structure is needed for backing up node information 01130 * during node promotion 01131 ****************************************************************/ 01132 typedef struct myNodeInfo_t { 01133 int indegree; 01134 int outdegree; 01135 int rank; 01136 Agnode_t *node; 01137 } myNodeInfo_t; 01138 01139 myNodeInfo_t *myNodeInfo; 01140 01141 01142 /* getMaxLevelNumber: 01143 * return the maximum level number assigned 01144 */ 01145 int getMaxLevelNumber(graph_t * g) 01146 { 01147 int max; 01148 Agnode_t *n; 01149 01150 max = ND_rank(agfstnode(g)); 01151 01152 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 01153 if (ND_rank(n) > max) 01154 max = ND_rank(n); 01155 } 01156 01157 return max; 01158 } 01159 01160 /* countDummyDiff: 01161 * return the difference in the count of dummy nodes before 01162 * and after promoting the node v 01163 */ 01164 static int countDummyDiff(graph_t * g, Agnode_t * v, int max) 01165 { 01166 int dummydiff = 0; 01167 Agedge_t *e; 01168 Agnode_t *u; 01169 int maxR = 0; 01170 int j; 01171 01172 for (j = 0; j < ND_in(v).size; j++) { 01173 e = ND_in(v).list[j]; 01174 u = agtail(e); 01175 01176 if (myNodeInfo[ND_id(u)].rank == myNodeInfo[ND_id(v)].rank + 1) { 01177 dummydiff += countDummyDiff(g, u, max); 01178 } 01179 } 01180 01181 if (myNodeInfo[ND_id(v)].rank + 1 < max 01182 || (ND_in(v).size == 0 && myNodeInfo[ND_id(v)].rank + 1 <= max)) 01183 myNodeInfo[ND_id(v)].rank += 1; 01184 01185 dummydiff = dummydiff - ND_in(v).size + ND_out(v).size; 01186 01187 01188 return dummydiff; 01189 } 01190 01191 /* applyPromotionHeuristic: 01192 * Node Promotion Heuristic 01193 * by Nikolov and Healy 01194 */ 01195 static void applyPromotionHeuristic(graph_t * g) 01196 { 01197 graph_t graphBkup = *g; 01198 Agnode_t *v; 01199 int promotions; 01200 01201 int max = getMaxLevelNumber(g); 01202 int count = 0; 01203 int nNodes = agnnodes(g); 01204 int i, j; 01205 01206 myNodeInfo = N_NEW(nNodes, myNodeInfo_t); 01207 myNodeInfo_t *myNodeInfoBak = N_NEW(nNodes, myNodeInfo_t); 01208 01209 for (v = agfstnode(g), i = 0; v; v = agnxtnode(g, v), i++) { 01210 myNodeInfo[i].indegree = ND_in(v).size; 01211 myNodeInfo[i].outdegree = ND_out(v).size; 01212 myNodeInfo[i].rank = ND_rank(v); 01213 myNodeInfo[i].node = v; 01214 ND_id(v) = i; 01215 01216 myNodeInfoBak[i] = myNodeInfo[i]; 01217 } 01218 01219 do { 01220 promotions = 0; 01221 01222 for (v = agfstnode(g); v; v = agnxtnode(g, v)) { 01223 if (ND_in(v).size > 0) { 01224 if (countDummyDiff(g, v, max) <= 0) { 01225 promotions++; 01226 01227 for (j = 0; j < nNodes; j++) { 01228 myNodeInfoBak[j] = myNodeInfo[j]; 01229 } 01230 01231 } else { 01232 for (j = 0; j < nNodes; j++) { 01233 myNodeInfo[j] = myNodeInfoBak[j]; 01234 } 01235 } 01236 } 01237 } 01238 count++; 01239 } while (count < max); 01240 01241 for (v = agfstnode(g); v; v = agnxtnode(g, v)) { 01242 ND_rank(v) = myNodeInfo[ND_id(v)].rank; 01243 } 01244 } 01245 01246 /* 01247 * LONGEST PATH ALGORITHM 01248 */ 01249 01250 /* allNeighborsAreBelow: 01251 * Return 1 if all the neighbors of n already ranked, else 0 01252 */ 01253 static int allNeighborsAreBelow(Agnode_t * n) 01254 { 01255 Agedge_t *e; 01256 graph_t *g = agraphof(n); 01257 int i; 01258 01259 //for (e = agfstout(g,n); e; e = agnxtout(g,e)) 01260 for (i = 0; i < ND_out(n).size; i++) { 01261 e = ND_out(n).list[i]; 01262 if (ED_edge_type(e) == VIRTUAL) { 01263 if (ED_to_orig(e) != NULL) 01264 e = ED_to_orig(e); 01265 else if (ND_node_type(aghead(e)) == VIRTUAL) 01266 continue; 01267 } 01268 01269 if (ND_pinned(aghead(e)) != 2) //neighbor of n is not below 01270 { 01271 return 0; 01272 } 01273 } 01274 01275 return 1; 01276 } 01277 01278 /* reverseLevelNumbers: 01279 * In Nikolov and Healy ranking, bottom layer ranking is 0 and 01280 * top layer ranking is the maximum. 01281 * Graphviz does the opposite. 01282 * This function does the reversing from Nikolov to Graphviz. 01283 */ 01284 static void reverseLevelNumbers(graph_t * g) 01285 { 01286 Agnode_t *n; 01287 int max; 01288 01289 max = getMaxLevelNumber(g); 01290 01291 //printf("max = %d\n", max); 01292 01293 //return; 01294 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 01295 ND_rank(n) = max - ND_rank(n); 01296 } 01297 } 01298 01299 /* doSameRank: 01300 * Maintain the same ranking constraint. 01301 * Can only be used with the Nikolov and Healy algorithm 01302 */ 01303 static void doSameRank(graph_t * g) 01304 { 01305 int i; 01306 for (i = 0; i < nNodeGroups; i++) { 01307 int j; 01308 01309 for (j = 0; j < nodeGroups[i].nNodes; j++) { 01310 if (ND_ranktype(nodeGroups[i].nodes[j]) == SAMERANK) //once we find a SAMERANK node in a group- make all the members of the group SAMERANK 01311 { 01312 int k; 01313 int r = ND_rank(UF_find(nodeGroups[i].nodes[j])); 01314 for (k = 0; k < nodeGroups[i].nNodes; k++) { 01315 ND_rank(nodeGroups[i]. 01316 nodes[(j + k) % nodeGroups[i].nNodes]) = r; 01317 } 01318 01319 break; 01320 } 01321 } 01322 } 01323 } 01324 01325 /* doMinRank: 01326 * Maintain the MIN ranking constraint. 01327 * Can only be used with the Nikolov and Healy algorithm 01328 */ 01329 void doMinRank(graph_t * g) 01330 { 01331 int i; 01332 for (i = 0; i < nNodeGroups; i++) { 01333 int j; 01334 01335 for (j = 0; j < nodeGroups[i].nNodes; j++) { 01336 if (ND_ranktype(nodeGroups[i].nodes[j]) == MINRANK) //once we find a MINRANK node in a group- make the rank of all the members of the group 0 01337 { 01338 int k; 01339 for (k = 0; k < nodeGroups[i].nNodes; k++) { 01340 ND_rank(nodeGroups[i]. 01341 nodes[(j + k) % nodeGroups[i].nNodes]) = 0; 01342 if (ND_ranktype 01343 (nodeGroups[i]. 01344 nodes[(j + k) % nodeGroups[i].nNodes]) != 01345 SOURCERANK) 01346 ND_ranktype(nodeGroups[i]. 01347 nodes[(j + 01348 k) % nodeGroups[i].nNodes]) = 01349 MINRANK; 01350 } 01351 01352 break; 01353 } 01354 } 01355 } 01356 } 01357 01358 /* getMaxRank: 01359 * Return the maximum rank among all nodes. 01360 */ 01361 static int getMaxRank(graph_t * g) 01362 { 01363 int i; 01364 node_t *v; 01365 int maxR = ND_rank(agfstnode(g)); 01366 for (v = agfstnode(g); v; v = agnxtnode(g, v)) { 01367 if (ND_rank(v) > maxR) 01368 maxR = ND_rank(v); 01369 } 01370 01371 return maxR; 01372 } 01373 01374 /* doMaxRank: 01375 * Maintain the MAX ranking constraint. 01376 * Can only be used with the Nikolov and Healy algorithm 01377 */ 01378 static void doMaxRank(graph_t * g) 01379 { 01380 int i; 01381 for (i = 0; i < nNodeGroups; i++) { 01382 int j; 01383 int maxR = getMaxRank(g); 01384 01385 for (j = 0; j < nodeGroups[i].nNodes; j++) { 01386 if (ND_ranktype(nodeGroups[i].nodes[j]) == MAXRANK) //once we find a MAXRANK node in a group- make the rank of all the members of the group MAX 01387 { 01388 int k; 01389 for (k = 0; k < nodeGroups[i].nNodes; k++) { 01390 ND_rank(nodeGroups[i]. 01391 nodes[(j + k) % nodeGroups[i].nNodes]) = maxR; 01392 if (ND_ranktype 01393 (nodeGroups[i]. 01394 nodes[(j + k) % nodeGroups[i].nNodes]) != 01395 SINKRANK) 01396 ND_ranktype(nodeGroups[i]. 01397 nodes[(j + 01398 k) % nodeGroups[i].nNodes]) = 01399 MAXRANK; 01400 } 01401 01402 break; 01403 } 01404 } 01405 } 01406 } 01407 01408 /* doSourceRank: 01409 * Maintain the SOURCE ranking constraint. 01410 * Can only be used with the Nikolov and Healy algorithm 01411 */ 01412 static void doSourceRank(graph_t * g) 01413 { 01414 int i; 01415 int flag = 0; 01416 01417 for (i = 0; i < nNodeGroups; i++) { 01418 int j; 01419 01420 for (j = 0; j < nodeGroups[i].nNodes; j++) { 01421 //once we find a SOURCERANK node in a group- make the rank of all the members of the group 0 01422 if (ND_ranktype(nodeGroups[i].nodes[j]) == SOURCERANK) { 01423 int k; 01424 for (k = 0; k < nodeGroups[i].nNodes; k++) { 01425 ND_rank(nodeGroups[i]. 01426 nodes[(j + k) % nodeGroups[i].nNodes]) = 0; 01427 ND_ranktype(nodeGroups[i]. 01428 nodes[(j + k) % nodeGroups[i].nNodes]) = 01429 SOURCERANK; 01430 } 01431 01432 flag = 1; 01433 break; 01434 } 01435 } 01436 } 01437 01438 if (!flag) 01439 return; 01440 01441 flag = 0; 01442 01443 //The SourceRank group might be the only group having rank 0. Check if increment of ranking of other nodes is necessary at all. 01444 for (i = 0; i < nNodeGroups; i++) { 01445 if (nodeGroups[i].nNodes > 0 01446 && ND_ranktype(nodeGroups[i].nodes[0]) != SOURCERANK 01447 && ND_rank(nodeGroups[i].nodes[0]) == 0) { 01448 flag = 1; 01449 break; 01450 } 01451 } 01452 01453 01454 if (!flag) 01455 return; 01456 01457 //Now make all NON-SourceRank nodes' ranking nonzero (increment) 01458 for (i = 0; i < nNodeGroups; i++) { 01459 if (nodeGroups[i].nNodes > 0 01460 && ND_ranktype(nodeGroups[i].nodes[0]) != SOURCERANK) { 01461 int j; 01462 01463 for (j = 0; j < nodeGroups[i].nNodes; j++) { 01464 ND_rank(nodeGroups[i].nodes[j])++; 01465 } 01466 } 01467 } 01468 } 01469 01470 /* doSinkRank: 01471 * Maintain the SINK ranking constraint. 01472 * Can only be used with the Nikolov and Healy algorithm 01473 */ 01474 static void doSinkRank(graph_t * g) 01475 { 01476 int i, max; 01477 int flag = 0; 01478 01479 max = getMaxRank(g); 01480 01481 01482 //Check if any non-sink node has rank = max 01483 for (i = 0; i < nNodeGroups; i++) { 01484 if (nodeGroups[i].nNodes > 0 01485 && ND_ranktype(nodeGroups[i].nodes[0]) != SINKRANK 01486 && ND_rank(nodeGroups[i].nodes[0]) == max) { 01487 flag = 1; 01488 break; 01489 } 01490 } 01491 01492 if (!flag) 01493 return; 01494 01495 for (i = 0; i < nNodeGroups; i++) { 01496 int j; 01497 01498 for (j = 0; j < nodeGroups[i].nNodes; j++) { 01499 if (ND_ranktype(nodeGroups[i].nodes[j]) == SINKRANK) //once we find a SINKRANK node in a group- make the rank of all the members of the group: max+1 01500 { 01501 int k; 01502 for (k = 0; k < nodeGroups[i].nNodes; k++) { 01503 ND_rank(nodeGroups[i]. 01504 nodes[(j + k) % nodeGroups[i].nNodes]) = 01505 max + 1; 01506 ND_ranktype(nodeGroups[i]. 01507 nodes[(j + k) % nodeGroups[i].nNodes]) = 01508 SINKRANK; 01509 } 01510 01511 break; 01512 } 01513 } 01514 } 01515 } 01516 01517 /* rank2: 01518 * Initial codes for ranking (Nikolov-Healy). 01519 * It's no longer used. 01520 */ 01521 void rank2(graph_t * g) 01522 { 01523 int currentLayer = 1; 01524 int nNodes = agnnodes(g); 01525 int nEdges = agnedges(g); 01526 int nPinnedNodes = 0, nSelected = 0; 01527 Agnode_t *n, **UArray; 01528 int USize = 0; 01529 int i, prevSize = 0; 01530 01531 UArray = N_NEW(nEdges * 2, Agnode_t *); 01532 01533 /* make all pinning values 0 */ 01534 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 01535 ND_pinned(n) = 0; 01536 } 01537 01538 while (nPinnedNodes != nNodes) { 01539 for (nSelected = 0, n = agfstnode(g); n; n = agnxtnode(g, n)) { 01540 if (ND_pinned(n) == 0) { 01541 if (allNeighborsAreBelow(n)) { 01542 ND_pinned(n) = 1; 01543 01544 UArray[USize] = n; 01545 USize++; 01546 01547 ND_rank(n) = currentLayer; 01548 nPinnedNodes++; 01549 nSelected++; 01550 } 01551 } 01552 } 01553 01554 if (nSelected == 0) //no node has been selected 01555 { 01556 currentLayer++; 01557 for (i = prevSize; i < USize; i++) { 01558 ND_pinned(UArray[i]) = 2; //pinning value of 2 indicates this node is below the current node under consideration 01559 } 01560 01561 prevSize = USize; 01562 } 01563 } 01564 01565 //Apply Nikolov's node promotion heuristic 01566 applyPromotionHeuristic(g); 01567 01568 //this is for the sake of graphViz layer numbering scheme 01569 reverseLevelNumbers(g); 01570 01571 computeNodeGroups(g); //groups of UF DS nodes 01572 01573 //modify the ranking to respect the same ranking constraint 01574 doSameRank(g); 01575 01576 //satisfy min ranking constraints 01577 doMinRank(g); 01578 doMaxRank(g); 01579 01580 //satisfy source ranking constraints 01581 doSourceRank(g); 01582 doSinkRank(g); 01583 01584 //Apply the FFDH algorithm to achieve better aspect ratio; 01585 applyPacking(g, 1); //achieve an aspect ratio of 1 01586 } 01587 #endif 01588 01589 /**************************************************************** 01590 * Initialize all the edge types to NORMAL 01591 ****************************************************************/ 01592 void initEdgeTypes(graph_t * g) 01593 { 01594 edge_t *e; 01595 node_t *n; 01596 int lc; 01597 01598 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 01599 for (lc = 0; lc < ND_in(n).size; lc++) { 01600 e = ND_in(n).list[lc]; 01601 ED_edge_type(e) = NORMAL; 01602 } 01603 } 01604 } 01605 01606 /* computeCombiAR: 01607 * Compute and return combinatorial aspect ratio 01608 * = 01609 * Width of the widest layer / Height 01610 * (in ranking phase) 01611 */ 01612 static double computeCombiAR(graph_t * g) 01613 { 01614 int i, maxLayerIndex; 01615 double maxW = 0; 01616 double maxH; 01617 double ratio; 01618 01619 computeLayerWidths(g); 01620 maxH = (nLayers - 1) * GD_ranksep(g); 01621 01622 for (i = 0; i < nLayers; i++) { 01623 if (maxW < 01624 layerWidthInfo[i].width + 01625 layerWidthInfo[i].nDummyNodes * GD_nodesep(g)) { 01626 maxW = 01627 layerWidthInfo[i].width + 01628 layerWidthInfo[i].nDummyNodes * GD_nodesep(g); 01629 maxLayerIndex = i; 01630 } 01631 maxH += layerWidthInfo[i].height; 01632 } 01633 01634 ratio = maxW / maxH; 01635 01636 return ratio; 01637 } 01638 01639 #ifdef UNUSED 01640 /* applyExpansion: 01641 * Heuristic for expanding very narrow graphs by edge reversal. 01642 * Needs improvement. 01643 */ 01644 void applyExpansion(graph_t * g) 01645 { 01646 node_t *sink = NULL; 01647 int i, k; 01648 edge_t *e; 01649 01650 computeLayerWidths(g); 01651 01652 for (i = 0; i < nLayers; i++) { 01653 if (layerWidthInfo[i].layerNumber == nLayers / 2) { 01654 k = i; 01655 break; 01656 } 01657 } 01658 01659 //now reverse the edges, from the k-th layer nodes to their parents 01660 for (i = 0; i < layerWidthInfo[k].nNodeGroupsInLayer; i++) { 01661 int p; 01662 nodeGroup_t *ng = layerWidthInfo[k].nodeGroupsInLayer[i]; 01663 for (p = 0; p < ng->nNodes; p++) { 01664 node_t *nd = ng->nodes[p]; 01665 01666 while (e = ND_in(nd).list[0]) { 01667 printf("Reversing edge: %s->%s\n", agnemeof(agtail(e)), 01668 agnameof(aghead(e))); 01669 reverse_edge(e); 01670 } 01671 01672 int j, l; 01673 node_t *v3; 01674 edge_t *e3; 01675 for (v3 = agfstnode(g); v3; v3 = agnxtnode(g, v3)) { 01676 for (e3 = agfstout(g, v3); e3; e3 = agnxtout(g, e3)) { 01677 if (ND_rank(aghead(e3)) > k && ND_rank(agtail(e3)) < k) { 01678 printf("Reversing long edge: %s->%s\n", 01679 agnameof(agtail(e3)), agnameof(aghead(e3))); 01680 reverse_edge(e3); 01681 } 01682 01683 } 01684 } 01685 01686 /*for (l = 0; l < nLayers; l++) { 01687 if (layerWidthInfo[l].layerNumber <= k) 01688 continue; 01689 01690 for (j = 0; j < layerWidthInfo[l].nNodeGroupsInLayer; j++) { 01691 int q; 01692 nodeGroup_t *ng2 = layerWidthInfo[l].nodeGroupsInLayer[j]; 01693 for (q = 0; q < ng2->nNodes; q++) { 01694 node_t *nd2 = ng2->nodes[q]; 01695 edge_t *e2; 01696 int s = 0; 01697 while (e2 = ND_in(nd2).list[s]) { 01698 if (ND_rank(agtail(e2)) < k) { 01699 printf("Reversing edge: %s->%s\n", 01700 agnameof(agtail(e2)), agnameof(aghead(e2))); 01701 getchar(); 01702 //reverse_edge(e2); 01703 } 01704 else s++; 01705 } 01706 } 01707 } 01708 } */ 01709 01710 if (sink == NULL) { 01711 int brFlag = 1; 01712 for (l = 0; l < nLayers && brFlag; l++) { 01713 for (j = 0; 01714 j < layerWidthInfo[l].nNodeGroupsInLayer 01715 && brFlag; j++) { 01716 int q; 01717 nodeGroup_t *ng2 = 01718 layerWidthInfo[l].nodeGroupsInLayer[j]; 01719 for (q = 0; q < ng2->nNodes && brFlag; q++) { 01720 node_t *nd2 = ng2->nodes[q]; 01721 if (ND_in(nd2).size == 0) { 01722 sink = nd2; 01723 brFlag = 0; 01724 } 01725 } 01726 } 01727 } 01728 01729 } 01730 01731 virtual_edge(nd, /*sink */ 01732 layerWidthInfo[0].nodeGroupsInLayer[0]->nodes[0], 01733 NULL); 01734 } 01735 } 01736 01737 //collapse_sets(g); 01738 } 01739 #endif 01740 01741 /* zapLayers: 01742 * After applying the expansion heuristic, some layers are 01743 * found to be empty. 01744 * This function removes the empty layers. 01745 */ 01746 static void zapLayers(graph_t * g) 01747 { 01748 int i, j; 01749 int start = 0; 01750 int count = 0; 01751 01752 /* the layers are sorted by the layer number. now zap the empty layers */ 01753 01754 for (i = 0; i < nLayers; i++) { 01755 if (layerWidthInfo[i].nNodeGroupsInLayer == 0) { 01756 if (count == 0) 01757 start = layerWidthInfo[i].layerNumber; 01758 count++; 01759 } else if (count && layerWidthInfo[i].layerNumber > start) { 01760 for (j = 0; j < layerWidthInfo[i].nNodeGroupsInLayer; j++) { 01761 int q; 01762 nodeGroup_t *ng = layerWidthInfo[i].nodeGroupsInLayer[j]; 01763 for (q = 0; q < ng->nNodes; q++) { 01764 node_t *nd = ng->nodes[q]; 01765 ND_rank(nd) -= count; 01766 } 01767 } 01768 } 01769 } 01770 } 01771 01772 /* rank3: 01773 * ranking function for dealing with wide/narrow graphs, 01774 * or graphs with varying node widths and heights. 01775 * This function iteratively calls dot's rank1() function and 01776 * applies packing (by calling the applyPacking2 function. 01777 * applyPacking2 function calls the reduceMaxWidth2 function 01778 * for partitioning the widest layer). 01779 * Initially the iterations argument is -1, for which rank3 01780 * callse applyPacking2 function until the combinatorial aspect 01781 * ratio is <= the desired aspect ratio. 01782 */ 01783 void rank3(graph_t * g, aspect_t * asp) 01784 { 01785 Agnode_t *n; 01786 int i; 01787 int iterations = asp->nextIter; 01788 01789 computeNodeGroups(g); /* groups of UF DS nodes */ 01790 01791 for (i = 0; i < iterations || iterations == -1; i++) { 01792 /* initialize all ranks to be 0 */ 01793 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 01794 ND_rank(n) = 0; 01795 } 01796 01797 /* need to compute ranking first--- by Network flow */ 01798 01799 rank1(g); 01800 01801 asp->combiAR = computeCombiAR(g); 01802 if (Verbose) 01803 fprintf(stderr, "combiAR = %lf\n", asp->combiAR); 01804 01805 01806 /* Uncomment the following codes, for working with narrow graphs */ 01807 #ifdef UNUSED 01808 if (combiAR < 0.8 * targetAR) { 01809 char str[20]; 01810 printf("Apply expansion? (y/n):"); 01811 scanf("%s", str); 01812 if (strcmp(str, "y") == 0) 01813 applyExpansion(g); 01814 break; 01815 } else 01816 #endif 01817 if (iterations == -1 && asp->combiAR <= asp->targetAR) { 01818 asp->prevIterations = asp->curIterations; 01819 asp->curIterations = i; 01820 01821 break; 01822 } 01823 /* Apply the FFDH algorithm to reduce the aspect ratio; */ 01824 applyPacking2(g); 01825 } 01826 01827 /* do network flow once again... incorporating the added edges */ 01828 rank1(g); 01829 01830 computeLayerWidths(g); 01831 zapLayers(g); 01832 asp->combiAR = computeCombiAR(g); 01833 } 01834 01835 #ifdef UNUSED 01836 /* NikolovHealy: 01837 * Nikolov-Healy approach to ranking. 01838 * First, use longest path algorithm. 01839 * Then use node promotion heuristic. 01840 * This function is called by rank4 function. 01841 */ 01842 static void NikolovHealy(graph_t * g) 01843 { 01844 int currentLayer = 1; 01845 int nNodes = agnnodes(g); 01846 int nEdges = agnedges(g); 01847 int nPinnedNodes = 0, nSelected = 0; 01848 Agnode_t *n, **UArray; 01849 int USize = 0; 01850 int i, prevSize = 0; 01851 01852 /************************************************************************ 01853 * longest path algorithm 01854 ************************************************************************/ 01855 UArray = N_NEW(nEdges * 2, Agnode_t *); 01856 01857 /* make all pinning values 0 */ 01858 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 01859 ND_pinned(n) = 0; 01860 } 01861 01862 while (nPinnedNodes != nNodes) { 01863 for (nSelected = 0, n = agfstnode(g); n; n = agnxtnode(g, n)) { 01864 if (ND_pinned(n) == 0) { 01865 if (allNeighborsAreBelow(n)) { 01866 ND_pinned(n) = 1; 01867 01868 UArray[USize] = n; 01869 USize++; 01870 01871 ND_rank(n) = currentLayer; 01872 nPinnedNodes++; 01873 nSelected++; 01874 } 01875 } 01876 } 01877 01878 if (nSelected == 0) //no node has been selected 01879 { 01880 currentLayer++; 01881 for (i = prevSize; i < USize; i++) { 01882 ND_pinned(UArray[i]) = 2; //pinning value of 2 indicates this node is below the current node under consideration 01883 } 01884 01885 prevSize = USize; 01886 } 01887 01888 } 01889 01890 /************************************************************************ 01891 * end of longest path algorithm 01892 ************************************************************************/ 01893 01894 //Apply node promotion heuristic 01895 applyPromotionHeuristic(g); 01896 01897 //this is for the sake of graphViz layer numbering scheme 01898 reverseLevelNumbers(g); 01899 01900 } 01901 01902 01903 /* rank4: 01904 * This function is calls the NikolovHealy function 01905 * for ranking in the Nikolov-Healy approach. 01906 */ 01907 void rank4(graph_t * g, int iterations) 01908 { 01909 int currentLayer = 1; 01910 int nNodes = agnnodes(g); 01911 int nEdges = agnedges(g); 01912 int nPinnedNodes = 0, nSelected = 0; 01913 Agnode_t *n, **UArray; 01914 int USize = 0; 01915 int i, prevSize = 0; 01916 01917 int it; 01918 printf("# of interations of packing: "); 01919 scanf("%d", &it); 01920 printf("it=%d\n", it); 01921 01922 computeNodeGroups(g); //groups of UF DS nodes 01923 01924 for (i = 0; i < it; i++) { 01925 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 01926 ND_rank(n) = 0; 01927 } 01928 01929 NikolovHealy(g); 01930 01931 edge_t *e; 01932 int cnt = 0; 01933 int lc; 01934 01935 01936 combiAR = computeCombiAR(g); 01937 printf("%d. combiAR = %lf\n", i, combiAR); 01938 01939 /* 01940 //modify the ranking to respect the same ranking constraint 01941 doSameRank(g); 01942 01943 //satisfy min ranking constraints 01944 doMinRank(g); 01945 doMaxRank(g); 01946 01947 //satisfy source ranking constraints 01948 doSourceRank(g); 01949 doSinkRank(g); 01950 */ 01951 01952 //Apply the FFDH algorithm to achieve better aspect ratio; 01953 applyPacking4(g); 01954 01955 } 01956 01957 } 01958 #endif 01959 01960 /* init_UF_size: 01961 * Initialize the Union Find data structure 01962 */ 01963 void init_UF_size(graph_t * g) 01964 { 01965 node_t *n; 01966 01967 for (n = agfstnode(g); n; n = agnxtnode(g, n)) 01968 ND_UF_size(n) = 0; 01969 }
1.7.5