Figure9.1 shows an implementation of Simulated Annealing for the TSP written using ParadiseEO. The associated classes for modelling and loading the TSP can be found in AppendixE.1.1.
This top level harness does provide an indication of the search logic being used, based upon the classes and parameters to constructors used. For example, the cooling strategy is clearly defined, with a recognisable starting temperature and cooling rate, though only examining the documentation or the library source code will indicate that it is a geometric cooling schedule. More unusual parame-ters are the final temperature and the delay of the cooling strategy for a number of steps at each temperature.
The perturbation operation is less clear, being provided by a neighbourhood generator. The name of the generator suggests that it will randomly swap cities in
1Generators provide a method for creating streams and on demand computation in imperative languages.
// Neighbours defined as swaped elements of a list.
typedef moIndexedSwapNeighbor<Route> Neighbor;
int main (int __argc, char * __argv []) { eo::rng.reseed(time(NULL));
Graph :: load (__argv [1]) ; // Problem instance Route solution; // solution data structure RouteInit init; // solution creator
// evaluate through modification not copy moFullEvalByModif<Neighbor>
Figure 9.1: The main file for a Simulated Annealing based solver, implemented using the ParadiseEO-MO library in C++. This is modified from source code originally downloaded from the ParadiseEO website, written by S´ebastien Cahon and Thomas Legrand.
the sequence, however an examination of the code in the class moRndWithoutRe-plNeighborhoodindicates the following logic:
• When first initialised set a maximum index counter to the size of the neigh-bourhood.
• When generating neighbours, pick a random index up to the current
maxi-118
mum, and swap with the current maximum.
• Reduce the current maximum by one.
The functions used for this code are reproduced in AppendixE.1.2. Due to the way that neighbours are generated, through selecting a random number up to an index counter, the ParadiseEO code must specify the size of the neighbourhood to be the number of cities in the TSP, despite Simulated Annealing only generating a single solution from each neighbourhood. This is hard coded for convenience on line 13 of Figure9.1.
It is not clear whether the search process will remain in a specific instance of a neighbourhood until a new solution is accepted, or create new neighbourhoods at each step of the process. An examination of the files moSAExplorer, for exploring a neighbourhood, and moLocalSearch the super class of moSA reveals the full process. At each step the algorithm will create a new neighbourhood, and draw the first randomly generated solution from it and then decide to move to it, or not.
The full code from the original classes can be found in AppendixE.1.2.
The search logic can be described as follows:
• The perturbation of a solution is to swap a random city in the sequence with a specific index, nominally the last.
• The cooling strategy is geometric, starting at 1000, reducing by ∗0.99 each time it is cooled, and remaining at each temperature for n (in this example 5) steps.
• The process terminates once n ∗ 1146 steps have been taken. The constant 1146 can be computed based upon the number of steps for the geometric progression to reach 0.01 with no delay.
• The movement choice is the standard function, as seen in the SA neighbour-hood explorer class.
The Haskell code is shown in Figure 9.2. While the Haskell implementation is almost as long as the C++ version, it provides a more detailed description of the search logic at the top level; including the separation of the termination logic, the selection of the best solution encountered, the basic geometric cooling strat-egy, the characteristic of holding at each temperature for a number of steps and the perturbation routine being used. The functions saChoose and geoCooling are
main:: IO() main= do
-- loading the problem problem← do g ← newStdGen
p← loadTSPFile TriangularMatrix "fl417.tsp"
-- and creating an initial solution
return$ randomiseRoute g p -- creating the perturbation operation perturbOp← do g ← donewStdGen
let n= numCities problem − 1 let rs= randomRs (0, n) g
return$ zipWith(swapCitiesOnIndex n) rs -- creating the strategy
g← newStdGen let strat xs
= zipWith4 (saChoose solutionValue)
(stretch 5 $ geoCooling (0.99 :: Double) 1000) (randoms g)
xs
(perturbOp xs)
-- running and terminating the strategy, getting the best -- solution, and printing it to screen
print$(solutionValue :: TSPProblem → Double) ◦ best◦
take(5 ∗ 1146) ◦ loopP strat$ problem
Figure 9.2: The main file for a Haskell implementation of the Simulated An-nealing TSP solver, following the same logic as the ParadiseEO version. In this implementation the cooling function has the same parameters as the ParadiseEO version, and to achieve the characteristic of holding at each temperature for 5 steps the basic pattern is stretched.
drawn in from the metaheuristic library, while the data model of the TSP, the load-ing routines and file parsers for TSPLIB and basic interactions are provided by the combinatorial problems library2.
120
Average Score Average Time
Cooling Delay 10 20 40 10 20 40
ParadiseEO 458000 445000 448000 1.12s 2.10s 4.05s Haskell 296000 257000 250000 0.96s 1.86s 3.68s Table 9.1: A runtime comparison of Simulated Annealing for TSP fl417, in Par-adiseEO and Haskell. Each has been computed with varying cooling delays, caus-ing a linear increase in the number of required computations. Each test was run 10 times, and the average, to 3sf is presented here. Times are presented to the nearest 100th of a second.