Contents¶
In this notebook, we will learn
- How to parallelize the solution of the traveling sales person problem
- The concept of search overhead
The traveling sales person (TSP) problem¶
Let us start by presenting the traveling sales person (ASP) problem and its solution using a branch and bound algorithm.
Problem statement¶
- Given a graph $G$ with a distance table $C$ and an initial node (i.e. a city) in the graph
- Compute the shortest route that visits all cities exactly once, without returning to the initial city.
Note that there we consider a version of the problem in which do not return to the initial city. However, the classic version of the problem includes returning to the initial city.
As for the ASP problem we represent the distance table as a matrix, where $C_{ij}$ is the distance from node $i$ to node $j$. Next figure shows the input and solution (output) of the TSP problem for a simple 4-node graph.
Branch and bound algorithm¶
In [1]:
using Distributed
In [2]:
if procs() == workers()
addprocs(4)
end
Out[2]:
4-element Vector{Int64}:
2
3
4
5
In [3]:
@everywhere function visited(city,hops,path)
for i = 1:hops
if path[i] == city
return true
end
end
return false
end
In [4]:
function tsp_serial_impl(connections,hops,path,current_distance,min_distance)
num_cities = length(connections)
if hops == num_cities
if current_distance < min_distance
return current_distance
end
else
current_city = path[hops]
next_hops = hops + 1
for (next_city,distance_increment) in connections[current_city]
if !visited(next_city,hops,path)
path[next_hops] = next_city
next_distance = current_distance + distance_increment
if next_distance < min_distance
return tsp_serial_impl(connections,next_hops,path,next_distance,min_distance)
end
end
end
end
min_distance
end
Out[4]:
tsp_serial_impl (generic function with 1 method)
In [5]:
function tsp_serial(connections,city)
num_cities = length(connections)
path=zeros(Int,num_cities)
hops = 1
path[hops] = city
current_distance = 0
min_distance = typemax(Int)
min_distance = tsp_serial_impl(connections,hops,path,current_distance,min_distance)
(;path=path,distance=min_distance)
end
Out[5]:
tsp_serial (generic function with 1 method)
In [6]:
connections = [
[(1,0),(4,39),(5,76), (6,78),(3,94),(2,97)],
[(2,0),(5,25),(4,58),(3,62),(1,97),(6,109)],
[(3,0),(6,58),(2,62),(4,68),(5,70),(1,94)],
[(4,0),(5,38),(1,39),(2,58),(3,68),(6,78)],
[(5,0),(2,25),(4,38),(3,70),(1,76),(6,104)],
[(6,0),(3,58),(1,78),(4,78),(5,104),(2,109)]
]
city = 1
tsp_serial(connections,city)
Out[6]:
(path = [1, 4, 5, 2, 3, 6], distance = 222)
In [10]:
@everywhere function tsp_dist_impl(connections,hops,path,current_distance,min_distance,max_hops,jobs_chnl,ftr_result)
num_cities = length(connections)
if hops == num_cities
if current_distance < min_distance
if ftr_result !== nothing
@spawnat 1 begin
result = fetch(ftr_result)
result.path .= path
result.min_distance_ref[] = current_distance
end |> wait
end
return current_distance
end
elseif hops <= max_hops
current_city = path[hops]
next_hops = hops + 1
for (next_city,distance_increment) in connections[current_city]
if !visited(next_city,hops,path)
path[next_hops] = next_city
next_distance = current_distance + distance_increment
if next_distance < min_distance
return tsp_dist_impl(connections,next_hops,path,next_distance,min_distance,max_hops,jobs_chnl,ftr_result)
end
end
end
else
if jobs_chnl !== nothing
put!(jobs_chnl,(;hops,path,current_distance))
end
end
min_distance
end
function tsp_dist(connections,city)
max_hops = 2
num_cities = length(connections)
path=zeros(Int,num_cities)
hops = 1
path[hops] = city
current_distance = 0
min_distance = typemax(Int)
jobs_chnl = RemoteChannel(()->Channel{Any}(10))
ftr_result = @spawnat 1 (;path,min_distance_ref=Ref(min_distance))
task = @async begin
tsp_dist_impl(connections,hops,path,current_distance,min_distance,max_hops,jobs_chnl,nothing)
for w in workers()
put!(jobs_chnl,nothing)
end
end
@sync for w in workers()
@spawnat w begin
max_hops = typemax(Int)
jobs_channel = nothing
while true
job = take!(jobs_chnl)
if job == nothing
break
end
hops = job.hops
path = job.path
current_distance = job.current_distance
tsp_dist_impl(connections,hops,path,current_distance,min_distance,max_hops,jobs_channel,ftr_result)
end
end
end
result = fetch(ftr_result)
(;path = result.path, distance = result.min_distance_ref[])
end
city = 1
tsp_dist(connections,city)
Out[10]:
(path = [1, 4, 5, 2, 3, 6], distance = 222)
In [ ]: