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 }