MODEL:
! Traveling Salesman Problem. Simple version.(TSPCUTx)
Find the shortest tour that visits each city exactly once.
Subtour elimination method. We add a row/cut/constraint
to cut off a subtour until a full tour is found;
! Keywords: TSP, Traveling Salesman, Cut Generation, Tour, Subtour Elimination;
SETS:
CITY;
LINK( CITY, CITY):
DIST, ! The distance matrix;
Y; ! Y( I, J) = 1 if link I, J is in tour;
SUBTOUR: TOURSIZE;
SXC(SUBTOUR,CITY): FLAG;
ENDSETS DATA:
SUBTOUR = 1..30; ! Max subtour cuts allowed;
CITY =
NYK ATL CHI CIN HOU LAX MON PHL PIT STL SND SNF;
! Distance matrix, it need not be symmetric;
DIST =
0 864 845 664 1706 2844 396 92 386 1002 2892 3032 !NYK;
864 0 702 454 842 2396 1196 772 714 554 2363 2679 !ATL;
845 702 0 324 1093 2136 764 764 459 294 2184 2187 !CHI;
664 454 324 0 1137 2180 798 572 284 338 2228 2463 !CIN;
1706 842 1093 1137 0 1616 1857 1614 1421 799 1521 2021 !HOU;
2844 2396 2136 2180 1616 0 2900 2752 2464 1842 95 405 !LAX;
396 1196 764 798 1857 2900 0 424 514 1058 2948 2951 !MON;
92 772 764 572 1614 2752 424 0 305 910 2800 2951 !PHL;
386 714 459 284 1421 2464 514 305 0 622 2512 2646 !PIT;
1002 554 294 338 799 1842 1058 910 622 0 1890 2125 !STL;
2892 2363 2184 2228 1521 95 2948 2800 2512 1890 0 500 !SND;
3032 2679 2187 2463 2021 405 2951 2951 2646 2125 500 0;!SNF;
ENDDATA
!-----------------------------------------------------------;
SUBMODEL TSP_CUT:
! Minimize total distance traveled;
MIN = TOURLEN;
TOURLEN = @SUM( LINK: DIST * Y);
! The Assignment constraints;
@FOR( CITY( K):
! It must be entered;
@SUM( CITY( I)| I #NE# K: Y( I, K)) = 1;
! It must be departed;
@SUM( CITY( J)| J #NE# K: Y( K, J)) = 1;
! Cannot visit self; Y( K, K) = 0 );
! Subtour cuts added so far;
@FOR( SUBTOUR(t) | t #LE# ICUT:
@SUM( CITY(I) | FLAG(t,i) #EQ# 1:
@SUM( CITY(J) | FLAG(t,j) #EQ# 1: Y(i,j))) <= TOURSIZE(t) - 1;
);
! The Y(i,j) must be 0 or 1;
@FOR( LINK(i,j): @BIN( Y(i,j)));
ENDSUBMODEL
CALC:
@SET("TERSEO",2); ! Turn off default output;
N = @SIZE( CITY);
MXCUTS = @SIZE(SUBTOUR); ! Max cuts we have space for;
ICUT = 0;
MORECUTS = 1; ! =0 if no more cuts;
@WHILE( MORECUTS :
! Loop over subtour cuts, ICUT;
! Solve current version;
@SOLVE( TSP_CUT);
ICUT = ICUT + 1;
@WRITE( @NEWLINE(1),'#',ICUT,' Obj value = ', TOURLEN, @NEWLINE(1));
! Turn this on to see intermediate solutions;
!@FOR( LINK(i,j) | Y(i,j) #GT# .01: @WRITE(' ',i,' ',j,' ',Y(i,j),@NEWLINE(1)) );
! Find subtour if any;
StartCity = 1; ! Start at city 1;
KURSTOP = StartCity;
@FOR( CITY(k): FLAG(ICUT,k) = 0); ! Start with no cities in cut;
@WRITE(' Begin tour at ', CITY(KURSTOP), @NEWLINE(1));
TOURSIZE(ICUT) = 1;
! Loop over cities KURSTOP to find subtour including 1;
FLAG(ICUT, KURSTOP) = 1; ! City KURSTOP is in the cut;
NotHome = 1;
@WHILE( NotHome:
@FOR(CITY(j) | Y(KURSTOP, j) #GT# 0.5: ! Find next stop;
@WRITE(' Next stop on tour= ', CITY(j),@NEWLINE(1));
@IFC( j #EQ# startCity:
NotHome = 0; ! Back home to startCity;
@ELSE
TOURSIZE(ICUT) = TOURSIZE(ICUT) + 1;
KURSTOP = j;
FLAG(ICUT,KURSTOP) = 1; ! KURSTOP is in the cut;
); ! end of IFC;
); ! end of for loop;
); ! end of while loop;
@IFC( TOURSIZE(ICUT) #EQ# N:
@WRITE(' Above is complete tour, so min length tour found.');
MORECUTS = 0;
@ELSE
@WRITE(' Add constraint to cut off above subtour.',@NEWLINE(1));
);
@IFC( ICUT #EQ# @SIZE(SUBTOUR):
@WRITE(' Exhausted memory.');
MORECUTS = 0;
);
); ! End loop over add cuts;
! (There are various refinements/complications of this basic idea
to make order of magnitude performance improvements);
ENDCALC
|