00001 #include "stdafx.h"
00002 #include <cmath>
00003 #include <cfloat>
00004 #include "Random.h"
00005 #include "LayoutOptimization.h"
00006 #include "GAOptimizer.h"
00007
00008 #define MAX_OPTIMIZATION_TRIES 50
00009 #define FITNESS_RELATIVE_ERROR_THRESHOLD 0.002
00010 #define FITNESS_AVERAGE_START -2e5
00011
00012 using GAOptimizer::IGenotype;
00013 using GAOptimizer::IProblem;
00014
00015 #ifdef max
00016 #undef max
00017 #endif
00018
00019 LayoutOptimizer::LayoutOptimizer( Graph * graph )
00020 {
00021 m_graph = graph;
00022 }
00023
00024 void LayoutOptimizer::optimize( LayoutOptimizerListener * listener, bool startFromScratch )
00025 {
00026 int i,j;
00027 int x = 0;
00028 int m = MAX_OPTIMIZATION_TRIES;
00029 int maxy = 0;
00030 LayoutOptimizerListener::ContinueAbortOrCurrent status = LayoutOptimizerListener::ContinueAbortOrCurrent::CONTINUE;
00031
00032 for( i=0; i<m_graph->getNumberOfSubGraphs() && status == LayoutOptimizerListener::CONTINUE; ++i )
00033 {
00034 LayoutOptProblem problem( m_graph, i, startFromScratch );
00035 GAOptimizer::Optimizer optimizer;
00036
00037 optimizer.init( &problem, 500, 20 );
00038
00039 double fitnessAverage = FITNESS_AVERAGE_START;
00040 LayoutSolution * best;
00041 for( j=0; j<m && status == LayoutOptimizerListener::CONTINUE; ++j )
00042 {
00043 optimizer.step(800);
00044 best = (LayoutSolution*)optimizer.getBest();
00045 double maxFitness = optimizer.getMaxFitness();
00046
00047 if (listener != NULL)
00048 status = listener->update( (int)(100 * (i*m+j) / double(m_graph->getNumberOfSubGraphs() * m)), best, maxFitness );
00049 fitnessAverage = fitnessAverage / 2 + maxFitness / 2;
00050 if (fabs((fitnessAverage - maxFitness) / maxFitness) < FITNESS_RELATIVE_ERROR_THRESHOLD)
00051 break;
00052 }
00053
00054 if ( status != LayoutOptimizerListener::ABORT )
00055 {
00056
00057 best->crop();
00058 best->move( x, 0 );
00059 x += (best->m_xmax - best->m_xmin);
00060 for( j=0; j<best->m_nodes.size(); ++j )
00061 {
00062 best->m_nodes[j].m_node->m_x = best->m_nodes[j].m_x;
00063 best->m_nodes[j].m_node->m_y = best->m_nodes[j].m_y;
00064 maxy = std::max(maxy, best->m_nodes[j].m_y + m_graph->m_nodes[j]->m_sy);
00065 }
00066 }
00067 }
00068
00069 if ( status != LayoutOptimizerListener::ABORT )
00070 {
00071
00072 int y = YMARGIN + maxy;
00073 int max_y_size = 0;
00074 x = XMARGIN;
00075 for( i=0; i<m_graph->m_nodes.size(); ++i )
00076 {
00077 if( !(m_graph->m_nodes[i]->m_connectedToOthers) )
00078 {
00079 if ((m_graph->m_nodes[i]->m_sx + XMARGIN + x > 600)
00080 && (x > 100))
00081 {
00082 x = XMARGIN;
00083 y += max_y_size + YMARGIN;
00084 max_y_size = 0;
00085 }
00086 m_graph->m_nodes[i]->m_x = x;
00087 m_graph->m_nodes[i]->m_y = y;
00088 x += m_graph->m_nodes[i]->m_sx + XMARGIN;
00089 max_y_size = std::max(max_y_size, m_graph->m_nodes[i]->m_sy);
00090 }
00091 }
00092 }
00093 }
00094
00095 LayoutOptProblem::LayoutOptProblem( Graph * graph, int subGraph, bool startFromScratch )
00096 {
00097 int i,j,k;
00098
00099 m_nodeNum = 0;
00100
00101 m_startFromScratch = startFromScratch;
00102
00103
00104 double area = 0;
00105 for( i=0; i<graph->m_nodes.size(); ++i )
00106 {
00107 Node * n = graph->m_nodes[i];
00108 if( n->m_subGraph == subGraph )
00109 {
00110 area += ((2*XMARGIN+n->m_sx) * (2*YMARGIN+n->m_sy));
00111
00112 n->m_nodeId = m_nodeNum;
00113
00114
00115
00116 m_nodes.push_back( n );
00117
00118
00119 for( j=0; j<n->m_edges.size(); ++j )
00120 {
00121 Edge * edgeToAdd = n->m_edges[j];
00122
00123
00124 for( k=0; k<m_edges.size(); ++k )
00125 if( edgeToAdd == m_edges[k] )
00126 break;
00127 if( k==m_edges.size())
00128 m_edges.push_back( edgeToAdd );
00129 }
00130
00131 m_nodeNum++;
00132 }
00133 }
00134 m_width = (int)(3 * sqrt( area ));
00135 m_height = m_width;
00136 }
00137
00138
00139 IGenotype * LayoutOptProblem::createRandomSolution(int i)
00140 {
00141 LayoutSolution * sol = new LayoutSolution(this);
00142 if( i<2 && !m_startFromScratch)
00143 sol->copyFromGraph();
00144 return sol;
00145 }
00146
00147 double LayoutOptProblem::evaluteSolution( IGenotype * solution )
00148 {
00149 LayoutSolution * sol = (LayoutSolution*)solution;
00150 return sol->getScore();
00151 }
00152
00153 LayoutSolution::LayoutSolution( LayoutOptProblem * problem )
00154 {
00155 m_problem = problem;
00156
00157 m_nodes.resize( problem->m_nodeNum );
00158 for( int i=0; i<problem->m_nodeNum; ++i )
00159 m_nodes[i].m_node = problem->m_nodes[i];
00160
00161 random();
00162 }
00163
00164 void LayoutSolution::derive( GAOptimizer::IGenotype * parent1, GAOptimizer::IGenotype * parent2 )
00165 {
00166 LayoutSolution* p1 = (LayoutSolution*)parent1;
00167 LayoutSolution* p2 = (LayoutSolution*)parent2;
00168
00169
00170 int n = m_nodes.size();
00171 if( Random::nextDouble() < 0.8 )
00172 {
00173 for( int i=0; i<n; ++i )
00174 {
00175 if( Random::nextDouble() < 0.5 )
00176 placeNodeNearPos(i, p1->m_nodes[i].m_x, p1->m_nodes[i].m_y );
00177 else
00178 placeNodeNearPos(i, p2->m_nodes[i].m_x, p2->m_nodes[i].m_y );
00179 }
00180 }
00181 else
00182 {
00183 for( int i=0; i<n; ++i )
00184 placeNodeNearPos(i, p1->m_nodes[i].m_x, p1->m_nodes[i].m_y );
00185 }
00186
00187
00188 mutate();
00189 }
00190
00191 void LayoutSolution::random()
00192 {
00193 for( int i=0; i<m_nodes.size(); ++i )
00194 placeNodeToRandomPos(i);
00195 }
00196
00197 void LayoutSolution::copyFromGraph()
00198 {
00199 for( int i=0; i<m_nodes.size(); ++i )
00200 {
00201 int x = m_nodes[i].m_node->m_x;
00202 int y = m_nodes[i].m_node->m_y;
00203 placeNodeNearPos(i, x, y );
00204 }
00205 }
00206
00207
00208 bool LayoutSolution::areConnectionsCrossed( Edge * e1, Edge * e2 )
00209 {
00210 if( e1 == e2 )
00211 return false;
00212
00213 calcEdgeEnds( e1 );
00214 calcEdgeEnds( e2 );
00215
00216 return areLinesCrossed( e1->x1, e1->y1, e1->x2, e1->y2, e2->x1, e2->y1, e2->x2, e2->y2 );
00217 }
00218
00219 bool LayoutSolution::areLinesCrossed( int x1, int y1, int x2, int y2, int xp1, int yp1, int xp2, int yp2 )
00220 {
00221 if( (x1==xp1 && y1==yp1) || (x1==xp2 && y1==yp2) || (x2==xp1 && y2==yp1) || (x2==xp2 && y2==yp2) )
00222 return false;
00223
00224 int diffX = x2-x1;
00225 int diffY = y2-y1;
00226 int diffXp = xp2-xp1;
00227 int diffYp = yp2-yp1;
00228
00229 return((((diffX*yp1-diffY*xp1)<(diffX*y1-diffY*x1))^((diffX*yp2-diffY*xp2)<(diffX*y1-diffY*x1))) &
00230 (((diffXp*y1-diffYp*x1)<(diffXp*yp1-diffYp*xp1))^((diffXp*y2-diffYp*x2)<(diffXp*yp1-diffYp*xp1))));
00231 }
00232
00233 int LayoutSolution::getDirViolations( Edge * e )
00234 {
00235 int violations = 0;
00236
00237 if( e->cannotStartToNorth && e->y2<e->y1+2*YMARGIN )
00238 violations++;
00239 else if( e->cannotEndFromNorth && e->y1<e->y2+2*YMARGIN )
00240 violations++;
00241
00242 if( e->cannotStartToEast && e->x2>e->x1-2*XMARGIN )
00243 violations++;
00244 else if( e->cannotEndFromEast && e->x1>e->x2-2*XMARGIN )
00245 violations++;
00246
00247 if( e->cannotStartToSouth && e->y2>e->y1-2*YMARGIN )
00248 violations++;
00249 else if( e->cannotEndFromSouth && e->y1>e->y2-2*YMARGIN )
00250 violations++;
00251
00252 if( e->cannotStartToWest && e->x2<e->x1+2*XMARGIN )
00253 violations++;
00254 else if( e->cannotEndFromWest && e->x1<e->x2+2*XMARGIN )
00255 violations++;
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274 return violations;
00275 }
00276
00277 void LayoutSolution::calcBoundingBox()
00278 {
00279 m_xmin = INT_MAX;
00280 m_ymin = INT_MAX;
00281 m_xmax = -INT_MAX;
00282 m_ymax = -INT_MAX;
00283
00284 int n = m_nodes.size();
00285 for( int i=0; i<n; ++i )
00286 {
00287 if( m_nodes[i].m_x-XMARGIN < m_xmin )
00288 m_xmin = m_nodes[i].m_x-XMARGIN;
00289 if( m_nodes[i].m_y-YMARGIN < m_ymin )
00290 m_ymin = m_nodes[i].m_y-YMARGIN;
00291 if( m_nodes[i].m_x+m_nodes[i].m_node->m_sx + XMARGIN > m_xmax )
00292 m_xmax = m_nodes[i].m_x+m_nodes[i].m_node->m_sx + XMARGIN;
00293 if( m_nodes[i].m_y+m_nodes[i].m_node->m_sy + YMARGIN > m_ymax )
00294 m_ymax = m_nodes[i].m_y+m_nodes[i].m_node->m_sy + YMARGIN;
00295 }
00296 }
00297
00298 void LayoutSolution::crop()
00299 {
00300 calcBoundingBox();
00301 move( -m_xmin, -m_ymin );
00302 }
00303
00304 void LayoutSolution::move( int dx, int dy )
00305 {
00306 calcBoundingBox();
00307 int n = m_nodes.size();
00308 for( int i=0; i<n; ++i )
00309 {
00310 m_nodes[i].m_x += dx;
00311 m_nodes[i].m_y += dy;
00312 }
00313 m_xmin += dx;
00314 m_ymin += dy;
00315 m_xmax += dx;
00316 m_ymax += dy;
00317 }
00318
00319 void LayoutSolution::calcEdgeEnds( Edge * e, int& x1, int& y1, int& x2, int& y2 )
00320 {
00321 if( e->m_nodeFrom->m_parent == NULL )
00322 {
00323 x1 = m_nodes[e->m_nodeFrom->m_nodeId].m_x + e->m_nodeFrom->m_sx / 2;
00324 y1 = m_nodes[e->m_nodeFrom->m_nodeId].m_y + e->m_nodeFrom->m_sy / 2;
00325 }
00326 else
00327 {
00328 x1 = m_nodes[e->m_nodeFrom->m_parent->m_nodeId].m_x + e->m_nodeFrom->m_x + e->m_nodeFrom->m_sx / 2;
00329 y1 = m_nodes[e->m_nodeFrom->m_parent->m_nodeId].m_y + e->m_nodeFrom->m_y + e->m_nodeFrom->m_sy / 2;
00330 }
00331 if( e->m_nodeTo->m_parent == NULL )
00332 {
00333 x2 = m_nodes[e->m_nodeTo->m_nodeId].m_x + e->m_nodeTo->m_sx / 2;
00334 y2 = m_nodes[e->m_nodeTo->m_nodeId].m_y + e->m_nodeTo->m_sy / 2;
00335 }
00336 else
00337 {
00338 x2 = m_nodes[e->m_nodeTo->m_parent->m_nodeId].m_x + e->m_nodeTo->m_x + e->m_nodeTo->m_sx / 2;
00339 y2 = m_nodes[e->m_nodeTo->m_parent->m_nodeId].m_y + e->m_nodeTo->m_y + e->m_nodeTo->m_sy / 2;
00340 }
00341 }
00342
00343 void LayoutSolution::calcEdgeEnds( Edge * e )
00344 {
00345 calcEdgeEnds( e, e->x1, e->y1, e->x2, e->y2 );
00346 }
00347
00348 bool LayoutSolution::isNodePlaceable( int node, int x, int y )
00349 {
00350 NodePos& n = m_nodes[node];
00351
00352
00353 if( x<XMARGIN || y<YMARGIN || x+n.m_node->m_sx+XMARGIN>=m_problem->m_width
00354 || y+n.m_node->m_sy+YMARGIN>=m_problem->m_height )
00355 return false;
00356
00357
00358 int x1 = x - XMARGIN;
00359 int y1 = y - YMARGIN;
00360 int sx1 = n.m_node->m_sx + 2 * XMARGIN;
00361 int sy1 = n.m_node->m_sy + 2 * YMARGIN;
00362 for( int i=0; i<m_nodes.size(); ++i )
00363 {
00364 if( i!=node)
00365 {
00366 NodePos& n2 = m_nodes[i];
00367
00368 int x2 = n2.m_x - XMARGIN;
00369 int y2 = n2.m_y - YMARGIN;
00370 int sx2 = n2.m_node->m_sx + 2 * XMARGIN;
00371 int sy2 = n2.m_node->m_sy + 2 * YMARGIN;
00372
00373 if( !((x2 + sx2 <= x1) ||
00374 (y2 + sy2 <= y1) ||
00375 (x2 >= x1 + sx1) ||
00376 (y2 >= y1 + sy1)))
00377 {
00378 return false;
00379 }
00380 }
00381 }
00382 return true;
00383 }
00384
00385 void LayoutSolution::placeNode( int node, int x, int y )
00386 {
00387 m_nodes[node].m_x = x;
00388 m_nodes[node].m_y = y;
00389 }
00390
00391 void LayoutSolution::removeNode( int node )
00392 {
00393 m_nodes[node].m_x = -1;
00394 m_nodes[node].m_y = -1;
00395 }
00396
00397 void LayoutSolution::placeNodeNearPos( int node, int xorg, int yorg )
00398 {
00399 int maxx = m_problem->m_width - m_nodes[node].m_node->m_sx - XMARGIN;
00400 int maxy = m_problem->m_height - m_nodes[node].m_node->m_sy - YMARGIN;
00401
00402 if( xorg<XMARGIN)
00403 xorg = XMARGIN;
00404 if( xorg>=maxx )
00405 xorg = maxx;
00406 if( yorg<YMARGIN)
00407 yorg = YMARGIN;
00408 if( yorg>=maxy )
00409 yorg = maxy;
00410
00411 int x = xorg;
00412 int y = yorg;
00413 double r = 10;
00414 int m = 0;
00415 int n = 0;
00416
00417 while( x<XMARGIN || x>=maxx || y<YMARGIN|| y>=maxy || !isNodePlaceable(node,x,y) )
00418 {
00419 x = (int)(xorg + r * Random::nextGaussian());
00420 y = (int)(yorg + r * Random::nextGaussian());
00421 ++n;
00422 if( n>4 )
00423 {
00424 n = 0;
00425 if(r<m_problem->m_width/2)
00426 r*=2;
00427 }
00428 m++;
00429 if( m > 200 )
00430 {
00431
00432 return;
00433 }
00434 }
00435 placeNode( node, x, y );
00436 }
00437
00438 void LayoutSolution::placeNodeToRandomPos( int node )
00439 {
00440 placeNodeNearPos( node, XMARGIN+Random::nextInt(m_problem->m_width-m_nodes[node].m_node->m_sx-2*XMARGIN),
00441 YMARGIN+Random::nextInt(m_problem->m_height-m_nodes[node].m_node->m_sy-2*YMARGIN) );
00442 }
00443
00444 void LayoutSolution::randomSwap()
00445 {
00446 int node1 = Random::nextInt(m_nodes.size());
00447 int node2 = Random::nextInt(m_nodes.size());
00448 int x1 = m_nodes[node1].m_x;
00449 int y1 = m_nodes[node1].m_y;
00450 int x2 = m_nodes[node2].m_x;
00451 int y2 = m_nodes[node2].m_y;
00452 removeNode(node1);
00453 removeNode(node2);
00454 placeNodeNearPos( node1, x2, y2 );
00455 placeNodeNearPos( node2, x1, y1 );
00456 }
00457
00458 void LayoutSolution::moveOneToRandomPos()
00459 {
00460 int node = Random::nextInt(m_nodes.size());
00461 removeNode( node );
00462 placeNodeToRandomPos( node );
00463 }
00464
00465 void LayoutSolution::selectRandomNodes( IntSet& nodes )
00466 {
00467 int n = Random::nextInt( m_nodes.size()/2 );
00468 for( int i=0; i<n; ++i )
00469 {
00470 int nodeInd = Random::nextInt( m_nodes.size() );
00471 nodes.insert( nodeInd );
00472 }
00473 }
00474
00475 void LayoutSolution::addSubGraphNodes( Node * act, IntSet& nodes, Node * prohibited, double cutoff )
00476 {
00477
00478 if( act == prohibited || nodes.find( act->m_nodeId ) != nodes.end() )
00479 return;
00480
00481 nodes.insert( act->m_nodeId );
00482
00483 for( int i=0; i<act->m_edges.size(); ++i )
00484 {
00485 Edge * e = act->m_edges[i];
00486
00487 Node * n1 = e->getTopLevelFrom();
00488 Node * n2 = e->getTopLevelTo();
00489
00490 if( n1!=act && n1!=prohibited )
00491 addSubGraphNodes( n1, nodes, prohibited, cutoff );
00492 if( n2!=act && n2!=prohibited )
00493 addSubGraphNodes( n2, nodes, prohibited, cutoff );
00494 }
00495 }
00496
00497 void LayoutSolution::selectSubGraph( IntSet& nodes )
00498 {
00499 Edge * edge = m_problem->m_edges[Random::nextInt(m_problem->m_edges.size())];
00500
00501 Node * act;
00502 Node * prohibited;
00503
00504 if( Random::nextDouble() < 0.5 )
00505 {
00506 act = edge->getTopLevelFrom();
00507 prohibited = edge->getTopLevelTo();
00508 }
00509 else
00510 {
00511 prohibited = edge->getTopLevelFrom();
00512 act = edge->getTopLevelTo();
00513 }
00514
00515 double cutoff = 0;
00516
00517
00518
00519 addSubGraphNodes( act, nodes, prohibited, cutoff );
00520 }
00521
00522 void LayoutSolution::moveSome()
00523 {
00524 IntSet nodes;
00525 if( Random::nextDouble() < 0.2 )
00526 selectRandomNodes( nodes );
00527 else;
00528 selectSubGraph( nodes );
00529
00530 int dx, dy;
00531 double r = 1 + 10 * Random::nextDouble();
00532
00533 if( Random::nextDouble() < 0.2 )
00534 r = 200;
00535
00536 if( Random::nextDouble() < 0.333 )
00537 {
00538 dx = (int)(r * Random::nextGaussian());
00539 dy = 0;
00540 }
00541 else if( Random::nextDouble() < 0.5 )
00542 {
00543 dx = 0;
00544 dy = (int)(r * Random::nextGaussian());
00545 }
00546 else
00547 {
00548 dx = (int)(r * Random::nextGaussian());
00549 dy = (int)(r * Random::nextGaussian());
00550 }
00551
00552 IntSet::iterator it;
00553 for( it=nodes.begin(); it!=nodes.end(); ++it )
00554 {
00555 int node = *it;
00556 m_nodes[node].m_tempx = m_nodes[node].m_x + dx;
00557 m_nodes[node].m_tempy = m_nodes[node].m_y + dy;
00558 removeNode( node );
00559 }
00560
00561 for( it=nodes.begin(); it!=nodes.end(); ++it )
00562 {
00563 int node = *it;
00564 placeNodeNearPos( node, m_nodes[node].m_tempx, m_nodes[node].m_tempy );
00565 }
00566 }
00567
00568 void LayoutSolution::moveOne()
00569 {
00570 int nodeInd = Random::nextInt( m_nodes.size() );
00571
00572 double r = 1 + 10 * Random::nextDouble();
00573 if( Random::nextDouble() < 0.2 )
00574 r = 200;
00575
00576 int dx = (int)(r * Random::nextGaussian());
00577 int dy = (int)(r * Random::nextGaussian());
00578 int x = m_nodes[nodeInd].m_x + dx;
00579 int y = m_nodes[nodeInd].m_y + dy;
00580
00581 removeNode( nodeInd );
00582 placeNodeNearPos( nodeInd, x, y );
00583 }
00584
00585 void LayoutSolution::mutate()
00586 {
00587 int r = Random::nextInt(8);
00588
00589 switch( r )
00590 {
00591 case 0:
00592 case 1:
00593 moveOneToRandomPos();
00594 break;
00595 case 2:
00596 case 3:
00597 moveOne();
00598 break;
00599 case 4:
00600 case 5:
00601 moveSome();
00602 break;
00603 case 6:
00604 case 7:
00605 randomSwap();
00606 break;
00607 }
00608 }
00609
00610 double LayoutSolution::getScore()
00611 {
00612 int i,j;
00613 int crossings = 0;
00614 int dirViolations = 0;
00615 double length = 0;
00616 int n = m_problem->m_edges.size();
00617 int m = m_nodes.size();
00618 double weightPointX = 0;
00619 double weightPointY = 0;
00620
00621 for( i=0; i<n; ++i )
00622 {
00623 Edge * e1 = m_problem->m_edges[i];
00624 calcEdgeEnds( e1 );
00625 dirViolations += getDirViolations( e1 );
00626 double dx = (e1->x1 - e1->x2);
00627 double dy = (e1->y1 - e1->y2);
00628
00629
00630 double d = sqrt(dx*dx + dy*dy);
00631
00632
00633 length += d;
00634 }
00635
00636 for( i=0; i<n-1; ++i )
00637 {
00638 Edge * e1 = m_problem->m_edges[i];
00639 for( j=i+1; j<n; ++j )
00640 {
00641 Edge * e2 = m_problem->m_edges[j];
00642 if( areConnectionsCrossed( e1, e2 ) )
00643 crossings++;
00644 }
00645 }
00646
00647 return -0.01*length - 1*crossings - 10*dirViolations;
00648 }