Merge pull request #20 from fverdugo/francesc

Changes after session 4
This commit is contained in:
Francesc Verdugo authored and GitHub committed 2023-09-15 17:34:32 +02:00
commit 6ce67fde1a
5 files changed
+1806 -723

No files matched your search

+90 -1
View File
@@ -50,7 +50,6 @@ heatmap(x,y,(i,j)->mandel(i,j,max_iters))
## Asynchronous programming in Julia
### NB2-Q1
Evaluating `compute_π(100_000_000)` takes about 0.25 seconds. Thus, the loop would take about 2.5 seconds since we are calling the function 10 times.
@@ -71,4 +70,94 @@ It will take about 3 seconds. The channel has buffer size 4, thus the call to `p
The channel is not buffered and therefore the call to `put!` will block. The cell will run forever, since there is no other task that calls `take!` on this channel.
## Distributed computing in Julia
### NB3-Q1
We send the matrix (16 entries) and then we receive back the result (1 extra integer). Thus, the total number of transferred integers in 17.
### NB3-Q2
Even though we only use a single entry of the matrix in the remote worker, the entire matrix is captured and sent to the worker. Thus, we will transfer 17 integers like in Question 1.
### NB3-Q3
The value of `x` will still be zero since the worker receives a copy of the matrix and it modifies this copy, not the original one.
### NB3-Q4
In this case, the code `a[2]=2` is executed in the main process. Since the matrix is already in the main process, it is not needed to create and send a copy of it. Thus, the code modifies the original matrix and the value of `x` will be 2.
## Distributed computing with MPI
### Exercise 1
```julia
using MPI
MPI.Init()
comm = MPI.Comm_dup(MPI.COMM_WORLD)
rank = MPI.Comm_rank(comm)
nranks = MPI.Comm_size(comm)
buffer = Ref(0)
if rank == 0
msg = 2
buffer[] = msg
println("msg = $(buffer[])")
MPI.Send(buffer,comm;dest=rank+1,tag=0)
MPI.Recv!(buffer,comm;source=nranks-1,tag=0)
println("msg = $(buffer[])")
else
dest = if (rank != nranks-1)
rank+1
else
0
end
MPI.Recv!(buffer,comm;source=rank-1,tag=0)
buffer[] += 1
println("msg = $(buffer[])")
MPI.Send(buffer,comm;dest,tag=0)
end
```
### Exercise 2
```julia
f = () -> Channel{Int}(1)
chnls = [ RemoteChannel(f,w) for w in workers() ]
@sync for (iw,w) in enumerate(workers())
@spawnat w begin
chnl_snd = chnls[iw]
if w == 2
chnl_rcv = chnls[end]
msg = 2
println("msg = $msg")
put!(chnl_snd,msg)
msg = take!(chnl_rcv)
println("msg = $msg")
else
chnl_rcv = chnls[iw-1]
msg = take!(chnl_rcv)
msg += 1
println("msg = $msg")
put!(chnl_snd,msg)
end
end
end
```
This is another possible solution.
```julia
@everywhere function work(msg)
println("msg = $msg")
if myid() != nprocs()
next = myid() + 1
@fetchfrom next work(msg+1)
else
@fetchfrom 2 println("msg = $msg")
end
end
msg = 2
@fetchfrom 2 work(msg)
```
File diff suppressed because it is too large. Load diff

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 160 KiB

+27 -6
View File
@@ -614,6 +614,27 @@
"take!(chnl)"
]
},
{
"cell_type": "markdown",
"id": "ff0da45d",
"metadata": {},
"source": [
"### This will not work!\n",
"\n",
"You really need remote channels to communicate different processes. Standard Channels would not work. For instance, the following code would block at the `take!`. Worker 4 will receive a different copy of the channel and will put values in it. The channel defined in the main process will remain empty and this will make the take! to block. \n",
"\n",
"```julia\n",
"chnl = Channel{Int}()\n",
"@spawnat 4 begin\n",
" for i in 1:5\n",
" put!(chnl,i)\n",
" end\n",
" close(chnl)\n",
"end\n",
"take!(chnl)\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "1b3f8dcc",
@@ -769,7 +790,7 @@
"source": [
"\n",
"<div class=\"alert alert-block alert-success\">\n",
"<b>Question (Q1):</b> How many integers are transferred between master and worker? Including both directions. \n",
"<b>Question (NB3-Q1):</b> How many integers are transferred between master and worker? Including both directions. \n",
"</div>\n",
"\n",
"\n",
@@ -803,7 +824,7 @@
"metadata": {},
"source": [
"<div class=\"alert alert-block alert-success\">\n",
"<b>Question (Q2):</b> How many integers are transferred between master and worker? Including both directions. \n",
"<b>Question (NB3-Q2):</b> How many integers are transferred between master and worker? Including both directions. \n",
"</div>\n",
"\n",
"\n",
@@ -838,7 +859,7 @@
"source": [
"\n",
"<div class=\"alert alert-block alert-success\">\n",
"<b>Question (Q3):</b> Which value will be the value of `x` ? \n",
"<b>Question (NB3-Q3):</b> Which value will be the value of `x` ? \n",
"</div>\n"
]
},
@@ -862,7 +883,7 @@
"metadata": {},
"source": [
"<div class=\"alert alert-block alert-success\">\n",
"<b>Question (Q4):</b> Which value will be the value of `x` ? \n",
"<b>Question (NB3-Q4):</b> Which value will be the value of `x` ? \n",
"</div>\n",
"\n",
"Which value will be the value of `x` ?"
@@ -1309,7 +1330,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Julia 1.9.3",
"display_name": "Julia 1.9.0",
"language": "julia",
"name": "julia-1.9"
},
@@ -1317,7 +1338,7 @@
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
"version": "1.9.3"
"version": "1.9.0"
}
},
"nbformat": 4,
+448 -50
View File
@@ -51,6 +51,36 @@
"- There are Julia bindings via the package MPI.jl https://github.com/JuliaParallel/MPI.jl"
]
},
{
"cell_type": "markdown",
"id": "99c6febb",
"metadata": {},
"source": [
"### What is MPI.jl ?\n",
"\n",
"- It is not a Julia implementation of the MPI standard\n",
"- It is a wrapper to the C interface of MPI\n",
"- You need a C MPI installation in your system\n",
"\n",
"\n",
"MPI.jl provides a convenient Julia API to access MPI. For instance, this is how you get the id (rank) of the current process.\n",
"\n",
"```julia\n",
"comm = MPI.COMM_WORLD\n",
"rank = MPI.Comm_rank(comm)\n",
"```\n",
"\n",
"Internally, MPI.jl uses `ccall` which is a mechanism that allows you to call C functions from Julia. In this, example we are calling the C function `MPI_Comm_rank` from the underlying MPI installation.\n",
"\n",
"```julia\n",
"comm = MPI.COMM_WORLD \n",
"rank_ref = Ref{Cint}()\n",
"ccall((:MPI_Comm_rank, MPI.API.libmpi), Cint, (MPI.API.MPI_Comm, Ptr{Cint}), comm, rank_ref)\n",
"rank = Int(rank_ref[])\n",
"```\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "82e6e98f",
@@ -81,6 +111,66 @@
"</div>"
]
},
{
"cell_type": "markdown",
"id": "a534e3a2",
"metadata": {},
"source": [
"### Basic information about MPI processes\n",
"\n",
"The following cells give information about MPI processes, such as the rank id, the total number of processes and the name of the host running the code respectively. Before calling this functions one needs to initialize MPI."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "96f7c14e",
"metadata": {},
"outputs": [],
"source": [
"using MPI\n",
"MPI.Init()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bd8232f5",
"metadata": {},
"outputs": [],
"source": [
"comm = MPI.COMM_WORLD\n",
"rank = MPI.Comm_rank(comm)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0befa408",
"metadata": {},
"outputs": [],
"source": [
"nranks = MPI.Comm_size(comm)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ff01adcf",
"metadata": {},
"outputs": [],
"source": [
"MPI.Get_processor_name()"
]
},
{
"cell_type": "markdown",
"id": "f1a502a3",
"metadata": {},
"source": [
"Note that this note notebook is not running with different MPI processes (yet). So using MPI will only make sense later when we add more processes."
]
},
{
"cell_type": "markdown",
"id": "133327e2",
@@ -104,6 +194,32 @@
"println(\"Hello, I am process $rank of $nranks processes!\")"
]
},
{
"cell_type": "markdown",
"id": "baddbba1",
"metadata": {},
"source": [
"### Hello world in C\n",
"\n",
"```C\n",
"#include <mpi.h>\n",
"#include <stdio.h>\n",
"int main(int argc, char** argv) {\n",
" MPI_Init(NULL, NULL);\n",
" int world_size;\n",
" MPI_Comm_size(MPI_COMM_WORLD, &world_size);\n",
" int world_rank;\n",
" MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);\n",
" char processor_name[MPI_MAX_PROCESSOR_NAME];\n",
" int name_len;\n",
" MPI_Get_processor_name(processor_name, &name_len);\n",
" printf(\"Hello from %s, I am rank %d of %d ranks!\\n\",\n",
" processor_name, world_rank, world_size);\n",
" MPI_Finalize();\n",
"}\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "e3901c57",
@@ -311,63 +427,14 @@
"\n",
"MPI provides collective communication functions for communication involving multiple processes. Some usual collective directives are:\n",
"\n",
"- `MPI.Scatter`: Distributes data from one process to all processes.\n",
"- `MPI.Gather`: Gathers data from all processes to a single process.\n",
"- `MPI.Scatter`: Distributes data from one process to all processes.\n",
"- `MPI.Bcast`: Broadcasts data from one process to all processes.\n",
"- `MPI.Barrier`: Synchronizes all processes.\n",
"\n",
"See more collective directives available from Julia here: https://juliaparallel.org/MPI.jl/stable/reference/collective/\n"
]
},
{
"cell_type": "markdown",
"id": "8b4254d1",
"metadata": {},
"source": [
"### Scatter\n",
"\n",
"The \"root\" rank contains a buffer (e.g., a vector) of values (one value for each rank in a communicator). Scatter sends one value to each rank (the root rank also receives a value). The root rank can be any process in a communicator."
]
},
{
"attachments": {
"g13389.png": {
"image/png": "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 truncated
}
},
"cell_type": "markdown",
"id": "74d5b606",
"metadata": {},
"source": [
"<div>\n",
"<img src=\"attachment:g13389.png\" align=\"left\" width=\"350\"/>\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5ea413fd",
"metadata": {},
"outputs": [],
"source": [
"@everywhere workers() begin\n",
" comm = MPI.Comm_dup(MPI.COMM_WORLD)\n",
" nranks = MPI.Comm_size(comm)\n",
" rank = MPI.Comm_rank(comm)\n",
" root = 0\n",
" rcv = Ref(0) \n",
" if rank == root\n",
" snd = [10*(i+1) for i in 1:nranks]\n",
" println(\"I am sending: $snd\")\n",
" else\n",
" snd = nothing\n",
" end \n",
" MPI.Scatter!(snd,rcv,comm;root)\n",
" println(\"I have received: $(rcv[])\")\n",
"end"
]
},
{
"cell_type": "markdown",
"id": "97dc2886",
@@ -414,6 +481,125 @@
"end"
]
},
{
"cell_type": "markdown",
"id": "8b4254d1",
"metadata": {},
"source": [
"### Scatter\n",
"\n",
"The root rank contains a buffer (e.g., a vector) of values (one value for each rank in a communicator). Scatter sends one value to each rank (the root rank also receives a value). The root rank can be any process in a communicator."
]
},
{
"attachments": {
"g13389.png": {
"image/png": "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 truncated
}
},
"cell_type": "markdown",
"id": "74d5b606",
"metadata": {},
"source": [
"<div>\n",
"<img src=\"attachment:g13389.png\" align=\"left\" width=\"350\"/>\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5ea413fd",
"metadata": {},
"outputs": [],
"source": [
"@everywhere workers() begin\n",
" comm = MPI.Comm_dup(MPI.COMM_WORLD)\n",
" nranks = MPI.Comm_size(comm)\n",
" rank = MPI.Comm_rank(comm)\n",
" root = 0\n",
" rcv = Ref(0) \n",
" if rank == root\n",
" snd = [10*(i+1) for i in 1:nranks]\n",
" println(\"I am sending: $snd\")\n",
" else\n",
" snd = nothing\n",
" end \n",
" MPI.Scatter!(snd,rcv,comm;root)\n",
" println(\"I have received: $(rcv[])\")\n",
"end"
]
},
{
"cell_type": "markdown",
"id": "ed8da7f9",
"metadata": {},
"source": [
"### Bcast (broadcast)\n",
"\n",
"Similar to scatter, but we send the same message to all processes."
]
},
{
"attachments": {
"g1657.png": {
"image/png": "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 truncated
}
},
"cell_type": "markdown",
"id": "26b56c81",
"metadata": {},
"source": [
"<div>\n",
"<img src=\"attachment:g1657.png\" align=\"left\" width=\"350\"/>\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4de15781",
"metadata": {},
"outputs": [],
"source": [
"@everywhere workers() begin\n",
" comm = MPI.Comm_dup(MPI.COMM_WORLD)\n",
" nranks = MPI.Comm_size(comm)\n",
" rank = MPI.Comm_rank(comm)\n",
" root = 0\n",
" buffer = Ref(0)\n",
" if rank == root\n",
" buffer[] = 20\n",
" println(\"I am sending: $(buffer[])\")\n",
" end \n",
" MPI.Bcast!(buffer,comm;root)\n",
" println(\"I have received: $(buffer[])\")\n",
"end"
]
},
{
"cell_type": "markdown",
"id": "6de37cc9",
"metadata": {},
"source": [
"### Barrier"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ff61f64a",
"metadata": {},
"outputs": [],
"source": [
"@everywhere workers() begin\n",
" comm = MPI.Comm_dup(MPI.COMM_WORLD)\n",
" sleep(rand(1:3))\n",
" MPI.Barrier(comm)\n",
" println(\"Done!\")\n",
"end"
]
},
{
"cell_type": "markdown",
"id": "ac9a5b52",
@@ -435,6 +621,218 @@
"\n"
]
},
{
"cell_type": "markdown",
"id": "26879a46",
"metadata": {},
"source": [
"### Blocking communication\n",
"\n",
"If we start a receive before a matching send, we will block in the call to `MPI.Recv!`. Run the next cell and note that the message is not printed since the process is blocked at `MPI.Recv!` waiting for a matching send."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "68aa9ada",
"metadata": {},
"outputs": [],
"source": [
"@spawnat 4 begin\n",
" buffer = Ref(0)\n",
" comm = MPI.COMM_WORLD\n",
" MPI.Recv!(buffer,comm;source=2-2,tag=0)\n",
" println(\"I have received $(buffer[]).\")\n",
"end;"
]
},
{
"cell_type": "markdown",
"id": "ace9c787",
"metadata": {},
"source": [
"If you run the next cell containing the corresponding send, the communication will take place."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e18347cd",
"metadata": {},
"outputs": [],
"source": [
"@spawnat 2 begin\n",
" buffer = Ref(2)\n",
" comm = MPI.COMM_WORLD\n",
" MPI.Send(buffer,comm;dest=4-2,tag=0)\n",
" println(\"I have send $(buffer[]). It is now safe to overwite the buffer.\")\n",
"end;"
]
},
{
"cell_type": "markdown",
"id": "0a038200",
"metadata": {},
"source": [
"### MPI does not integrate well with Julia Tasks\n",
"\n",
"MPI blocks without yielding (we cannot switch to other Julia tasks). Run next cell:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d7da7d27",
"metadata": {},
"outputs": [],
"source": [
"@spawnat 4 begin\n",
" buffer = Ref(0)\n",
" comm = MPI.COMM_WORLD\n",
" MPI.Recv!(buffer,comm;source=2-2,tag=0)\n",
" println(\"I have received $(buffer[]).\")\n",
"end;"
]
},
{
"cell_type": "markdown",
"id": "530b673b",
"metadata": {},
"source": [
"Now try to spawn other tasks on process 4 by running next cell. This task will not be served yet."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ae1165ac",
"metadata": {},
"outputs": [],
"source": [
"@spawnat 4 println(\"Hello!\");"
]
},
{
"cell_type": "markdown",
"id": "c722d71e",
"metadata": {},
"source": [
"We first need to unlock the receive with a matching send. Then the task printing \"Hello!\" will be finally served."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9da6f76e",
"metadata": {},
"outputs": [],
"source": [
"@spawnat 2 begin\n",
" buffer = Ref(2)\n",
" comm = MPI.COMM_WORLD\n",
" MPI.Send(buffer,comm;dest=4-2,tag=0)\n",
" println(\"I have send $(buffer[]). It is now safe to overwite the buffer.\")\n",
"end;"
]
},
{
"cell_type": "markdown",
"id": "6f4fa1cd",
"metadata": {},
"source": [
"### Non-blocking communication"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d6e2fbbf",
"metadata": {},
"outputs": [],
"source": [
"@spawnat 4 begin\n",
" buffer = Ref(0)\n",
" comm = MPI.COMM_WORLD\n",
" req = MPI.Irecv!(buffer,comm;source=2-2,tag=0)\n",
" println(\"Not yet safe to read the buffer\")\n",
" MPI.Wait(req)\n",
" println(\"I have received $(buffer[]).\")\n",
"end;"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4aa426ff",
"metadata": {},
"outputs": [],
"source": [
"@spawnat 2 begin\n",
" buffer = Ref(2)\n",
" comm = MPI.COMM_WORLD\n",
" req = MPI.Isend(buffer,comm;dest=4-2,tag=0)\n",
" println(\"Not yet safe to write the buffer\")\n",
" MPI.Wait(req)\n",
" println(\"I have send $(buffer[]). It is now safe to overwite the buffer.\")\n",
"end;"
]
},
{
"cell_type": "markdown",
"id": "30d9a970",
"metadata": {},
"source": [
"### Combining Julia Tasks and non-blocking communication\n",
"\n",
"We can implement our own blocking mechanism that combines well with the Julia task scheduler. This can be done by testing on the request in a while loop. The `yield` in the loop provides the opportunity of other tasks to run.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "768b2439",
"metadata": {},
"outputs": [],
"source": [
"@spawnat 4 begin\n",
" buffer = Ref(0)\n",
" comm = MPI.COMM_WORLD\n",
" req = MPI.Irecv!(buffer,comm;source=2-2,tag=0)\n",
" while MPI.Test(req) == false\n",
" yield()\n",
" end\n",
" println(\"I have received $(buffer[]).\")\n",
"end;"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "073d138c",
"metadata": {},
"outputs": [],
"source": [
"@spawnat 4 println(\"Hello!\");"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "97bc403f",
"metadata": {},
"outputs": [],
"source": [
"@spawnat 2 begin\n",
" buffer = Ref(2)\n",
" comm = MPI.COMM_WORLD\n",
" req = MPI.Isend(buffer,comm;dest=4-2,tag=0)\n",
" while MPI.Test(req) == false\n",
" yield()\n",
" end\n",
" println(\"I have send $(buffer[]). It is now safe to overwite the buffer.\")\n",
"end;"
]
},
{
"cell_type": "markdown",
"id": "750fdacb",
+458 -403
View File
@@ -154,33 +154,36 @@
},
{
"cell_type": "markdown",
"id": "40027c85",
"id": "f2b70f85",
"metadata": {},
"source": [
"### Input distance"
"### Nearest-city first heuristic"
]
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"id": "a50706bc",
"metadata": {},
"outputs": [],
"source": [
"function sort_neighbors(C)\n",
" n = size(C,1)\n",
" map(1:n) do i\n",
" Ci = C[i,:]\n",
" cities = sortperm(Ci)\n",
" distances = Ci[cities]\n",
" collect(zip(cities,distances))\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2eeecdd6",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"4×4 Matrix{Int64}:\n",
" 0 2 3 2\n",
" 2 0 4 1\n",
" 3 4 0 3\n",
" 2 1 3 0"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"outputs": [],
"source": [
"C = [\n",
" 0 2 3 2\n",
@@ -190,73 +193,6 @@
"]"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "03cac0a1",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"4-element Vector{Vector{Tuple{Int64, Int64}}}:\n",
" [(1, 0), (2, 2), (3, 3), (4, 4)]\n",
" [(2, 0), (4, 1), (1, 2), (3, 3)]\n",
" [(3, 0), (1, 3), (2, 3), (4, 10)]\n",
" [(4, 0), (2, 1), (1, 4), (3, 10)]"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"C = [\n",
" [(1,0), (2,2), (3,3), (4,4)],\n",
" [(2,0), (4,1), (1,2), (3,3)],\n",
" [(3,0), (1,3), (2,3), (4,10)], \n",
" [(4,0), (2,1), (1,4), (3,10)]\n",
"]"
]
},
{
"cell_type": "markdown",
"id": "f2b70f85",
"metadata": {},
"source": [
"### Nearest-city first heuristic"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "a50706bc",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"sort_neighbors (generic function with 1 method)"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function sort_neighbors(C)\n",
" n = size(C,1)\n",
" map(1:n) do i\n",
" Ci = C[i,:]\n",
" cities = sortperm(Ci)\n",
" distances = Ci[cities]\n",
" collect(zip(cities,distances))[2:end]\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -264,7 +200,33 @@
"metadata": {},
"outputs": [],
"source": [
"connections = sort_neighbors(C)"
"C_sorted = sort_neighbors(C)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "00608e1d",
"metadata": {},
"outputs": [],
"source": [
"city = 3\n",
"C_sorted[city]"
]
},
{
"attachments": {
"g25091.png": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABUcAAAHMCAYAAAD25xnyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAewgAAHsIBbtB1PgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7N13uCRVmfjx751hBgaGIQ05CgIqGQRRQAxgQBF1F3FNGHH9gWtEMaC4BsCwiu6qKCYMu2KWRddFZUBERZLknDMIDMwMDMzM+f1xbi99q6q7b3dX1enw/TxPPbv3zr3nvI23qqvfOud9J5AkjZNVgWcAOwPbANsCmwBzgdUmj/uBJcBDwHXAVZPHn4BLgVB71JIkSZIkSZLUg02Bo4AzgUeIyc1ej7uBU4B/AubU+SIkSZIkSZIkaTpmAq8Afgssp7+EaKtjIXASsGNNr0mSJEmSJEmSWpoFvJG4Db6KhGjRsQL4BbBHDa9PkiRJkqTSTKQOQJJUmn2ALwPbT+Nn/w5cAlwN3Dj59UPAMmJd0rnAesS6pNsA2wErdxgzAN8DjgTu6jp6SZIkSZIkSerSPOCbxBWc7VZ3LgDeSdwG3+3DsVWB/YHjiMnUditJ7wfe3PvLkSRJkiRJkqTOdiWu/mzXQOmjwBYlzjkB7Av8gLjStNXcPwBWL3FeSZIkSZIkSQLgUFp3n7+LuEp0tYpj2IrYlKlV06ergK0rjkGSJEmSJEnSGHkHxQnJFcDJwPya49kF+FNBPAG4F9iz5ngkSZIkSZIkjaBPUpyEvBV4ZsK4ZgBHU7zV/kFg73ShSZIkSZIkSRp276E4Mfq/wLoJ42q2L3AnxY2adkgYlyRJkiRJkqQh9VqKO9KfDMxKGFeRrYBrycd6G7BZwrgkSZIkSZIkDZmdgIfJJxu/ROweP4jWBy4lH/OfGbxkriRJkiRJkqQBtBpwOfkk4/eJdT4H2UbADeRj/2zKoCRJkiRJkiQNhxPJJxd/y/CsvnwysJCp8a8AnpMyKEmSJEmSBnUrpiQp2p24Db15hehdwM7Epkf9mg88FdgOWKnp+4uA/yhh/IaDgVMy37sa2BFYWuI8kiRJkiRJkkbATOAC8isun93jePOAvYF3EJs4XUZxg6dG46SyfbVgnqMqmEeSJEmSJEnSkHsV+WTiST2O9YWCsdodVSRH502O2zzPQmDNCuaSJEmSJEmSNKQmgIuZmkj8O3EbfC++RfrkKMCrC+b6cEVzSZIkSZIkSRpCLyGfRPxAH+O1So4uB64EbqSe5OgEcFFmrnuAVSuaT5IkSZIkSdKQOZWpCcQH6G/7eSM5evvk2McABwLrTP77v1NPchTgEPJJ2ldXOJ8kSZIkSZKkIbEe8ChTk4fH9znmlsSan63UmRydAVyXme9/KpxPkiRJkqRCM1IHIEnKOQSYlfned/oc83rgwT7HKMsK4LuZ7+0HbJAgFkmSJEnSGDM5KkmD5wWZr88DLk8RSIW+S1wx2jAT2D9RLJIkSZKkMWVyVJIGy0rA3pnv/SJFIBW7jnzC99kpApEkSZIkjS+To5I0WJ5KvjboGSkCqcHvM18/J0kUkiRJkqSxZXJUkgbLjpmvlwDnpgikBgsyX28OrJkgDkmSJEnSmDI5KkmDZdvM11cDj6UIpAaXFXxvm9qjkCRJkiSNLZOjkjRYssnBq5JEUY/rySd+s8lhSZIkSZIqY3JUkgbLhpmvr08SRT0eA27JfC/7+iVJkiRJqozJUUkaLKtnvl6YJIr6ZF/f3CRRSJIkSZLGkslRSRos2eTooiRR1OehzNfzkkQhSZIkSRpLJkclabDMynz9aJIo6rM08/XsJFFIkiRJksaSyVFJGiyLM1+P+jbz7ErZ7EpSSZIkSZIqY3JUkgZLNjk46snR7DZ6k6OSJEmSpNqYHJWkwfJA5usNkkRRn/UyX496AypJkiRJ0gAxOSpJg+X6zNfbJImiHmsD8zPfuzZFIJIkSZKk8WRyVJIGy1WZr7dNEkU9il7b1bVHIUmSJEkaWyZHJWmwZJOjmwIbpQikBk/LfP0IcGOCOCRJkiRJY8rkqCQNlnOAkPnes1MEUoPnZr7+M7A8RSCSJEmSpPFkclSSBssdwJWZ7+2fIpCKzQaemfneGSkCkSRJkiSNL5OjkjR4fp/5+qXAnBSBVOhFwLzM97KvW5IkSZKkSpkclaTB85PM12sAL0kRSIVem/n6duBPKQKRJEmSJI0vk6OSNHguBO7LfO9tKQKpyKbElaPNfoL1RiVJkiRJkqSx9QTgBGARsSlT9tirwrn/PTPXbRXO9SXyr20xcCKwTYXzSpIkSZIkSRowuwInA49RnBRtHP/T5zxXANe1OBZm5lrW5mevA57VYwwbA0to/RqXA6cCT+9xfEmSJEmSJEkDbgaxjuiZtE+IZo+D+pjzkS7nancc0GMM/9XFHAuAFwMTPc4lSZIkSZIkaYDMBl4HXEbn5OBl5FeT3gis1uPcqZOj+xWMk12xWnRcA7wDmNPDnJIkSZIkSZISW4OY4LuNzsnAs4EDiSsmP1nw7yf1GEPK5OhawA2ZMZYBOwN7E7fSr+gw513AMcA6Xc4tSZIkSZIkKYEtad9kqXEsJdYd3T7z+3OINT6zP/+6GmIvywTwc/Kv4fOZn9ua+N/q4YKfbT4eIf632raG2CVJkiRJkiR1aTem12RpITEhuEmbsfYnNipq/r3FwDMqir1sx5F/3dcB81r8/PrEFaJ/L/i95qPRvGlY/jtIkiRJkiRJI2sGcTv86XTeln4D8H7idvvp+FTBGA8AO5UXfiX+H/m4HwX2nMbvzgUOA64uGCN7nEdcTTuz3PAlSZIkSZIktbMyMTF3OZ2TeBdM/uxKXc6xEnBWwXh3Arv0/Qqq8W6K64i+vctxGknnPxWMlT2uJdZ2XbX/8CVJkiRJkiS1Mp+4+rNTk6UVxNWkB/Y53zoUJ2AXAs/tc+wyzQA+Q/F/ixP6HHu6zZvuJm7Nn9/nfJIkSZIkSZKaNJosLWZ6TZa2K3HujYEbC+ZaRkwGzihxrl7MB35N8X+P/6S8+BrNm5a0mCvbvOlJJc0rSZIkSZIkjaVGk6VltE/IPUBM3G1cURxPpLiDfQB+Azyhonk7OZDWq2h/AMyuYM71iEnhe1vMm23etFcFMUiSJEmSJEkjqVHv8mw617u8nljvcrUa4toAuLBFHEuADwNzaogD4kraX7aIJQCfp/oVrasRmzdd1SYOmzdJkiRJkiRJ09BosnQFnZNt59Nbk6V+zQNOaRPXHcB7iV3fq7AN8C1i9/mi+R8G3lrR3K00ktnntIip+bgOmzdJkiRJkiRJ/2dd4jbte5jeNu39kkQ51duIich22/y/AexL/ys45wGHAr8j/jdoNedVwM59ztWvvYnJ405lEO4BjgM2ShPmqAsTENaF8BQIz4TwEgjPhvBkCK7elSRJkiRJGgBbMb0mS40GP09JE2ZL2wK/pfNqyb8DPwGOAJ4FbNhmzJWIjY8OBD5GLC3QapVo41gKfIp6SgtM1xPprnnTk9OEOUrCcyB8GMJpEP4OIbQ4FkH4JYQXpY5YkiRJkiRpHHXbZGnQVxceQutmTa2ORcRGSlcSSwRcB9xN50Ro9jiNwe4K323zpkFYFTykwuI2CdFWx6/jClNJkiRJkiRVqVGX8o90Tvg16lIO0krITmYABwOX0V1ys5djBTGRuEctr6wcjXqyV9L59TWaN9VdT3bI5ZKjSyFcCeGvEP4C4RoIywsSpJdCWDN19JIkSZIkSaNo3JJiE8Azga8TV76WmRS9AvgQsHltr6Z83STJr2f4kuQJhbuJW+rfBWF7CmuLhrUn//3+TIL0xPrjlSRJkiRJGl1up46J4WcDHycmAxfRXTL0duCnxFqlg1ZvtQyjVl4hsdDFQ4WwPYSHmpKjj0BYo7rYJEmSJEmSxoONeNrbFHgucYXsl8n/d3klsDswTomq6TbmWspgNuYaUuG4zOrRF6aOSJIkSZIkaVjtDZxC51WAdwPH0b5j+7h4Mfn/PsNcUqBf6wLvJ66Y7VRz9XTi9nz1LLwokxx9Q+qIJEmSJEmShkmjfuQ5dN4ifi2xfuSqSSIdTCZHizXq1F5B57+r8xn+OrWJhBdkkqOHpI5IkiRJkiSVKqwKYWMIG0JYJXU0I2QucBhwFZ2TV40mSwXNYcaeydH2Gsn3s+n8d2bzpq6FD2eSo09MHZEkSZIkSepL2BLC4RB+BuHGzAf/AOEmCN+fXDE1I3W0Q6jRZOnvtE9UNZos7ZUkyuFhcnT6ptu8aSGxfunGacIcFmETCPc0XRt/lzoiSZIkSZLUs/CPEP5UkAxtd5wLwcYu07M13TVZelKaMIeOydHubUl3zZu2SxPmoApzILwGwi1N18J7IGyTOjJJkiRJktSzcGqbJOgyCI+1+LeFEPZIHf0A25u4AnQF7RNRdxNXlM5PEuXwMjnau/nE5k230f5vc0ybN4V1IJzedJwJ4VIISzPXwL9C8GGGJEmSJEnDbUpy9AYIn4LwPAhNybqwCYQjINyWSQ7cCWHNdLEPnEadxz/RPukUsMlSv0yO9q/RvOlyOv+9XsDYNG8KG3VYOX8HhFdDmEgdqSRJ0pjZBHgu8DbgQ8BxwJeBr07+/x8GDgf2BzYHvF+TJE1HOBXCAgj70bGWaJgP4aJMouATtYQ52OYSE5030jnJdDZwMDZZ6pfJ0fI0kvqn0/nv9wbiqtMRfijSMTkaJlfUnwxhg9TRSpIkjbDtgXcCvwQeoPO9avZ4EDgNeC+wS82xS5KGR9iiy5/fOrO99JpKwhoO69Ndk6WnJ4lyNJkcrcauxHqjj9H+b7rRvGmTNGFWKaw8+bCocRwA4VAIJ7RYPW9tVkmSpPJsSExmXkz3ydBOx5XEFaeb1fZqJEmjKvxPJkGwWuqIarYNMTH0MO3ffB8mJpq2TRPmSDM5Wq0nEP/GF9H+b7zRvGn7NGHWLcyC8FEIK5quf9dBWD11ZJIkSUOuLine truncated
}
},
"cell_type": "markdown",
"id": "7384a305",
"metadata": {},
"source": [
"<div>\n",
"<img src=\"attachment:g25091.png\" align=\"left\" width=\"525\"/>\n",
"</div>"
]
},
{
@@ -272,261 +234,228 @@
"id": "4f8cf018",
"metadata": {},
"source": [
"### Implementation"
"### Loop over all paths"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "d00beaba",
"execution_count": null,
"id": "2ddc2ec1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(path, distance, min_distance) = ([1, 2, 4, 3], 13, 13)\n",
"(path, distance, min_distance) = ([1, 3, 2, 4], 7, 7)\n"
]
},
{
"data": {
"text/plain": [
"7"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"outputs": [],
"source": [
"function tsp_serial(C,city)\n",
" #connections = sort_neighbors(C)\n",
" num_cities = length(connections)\n",
"function visital_all_paths(C_sorted,city)\n",
" num_cities = length(C_sorted)\n",
" path=zeros(Int,num_cities)\n",
" hops = 1\n",
" path[hops] = city\n",
" distance = 0\n",
" min_distance = typemax(Int)\n",
" tsp_serial_recursive!(connections,hops,path,distance,min_distance)\n",
" visital_all_paths_recursive!(C_sorted,hops,path)\n",
"end\n",
"function tsp_serial_recursive!(connections,hops,path,distance,min_distance)\n",
" num_cities = length(connections)\n",
" if hops == num_cities\n",
" min_distance = min(distance,min_distance)\n",
" @show path, distance, min_distance\n",
" return min_distance\n",
" else\n",
"function visital_all_paths_recursive!(C_sorted,hops,path)\n",
" num_cities = length(C_sorted)\n",
" if hops != num_cities\n",
" city = path[hops]\n",
" connections = C_sorted[city]\n",
" next_hops = hops + 1\n",
" current_city = path[hops]\n",
" for (next_city,distance_increment) in connections[current_city]\n",
" if !visited(next_city,hops,path)\n",
" for (next_city,distance_increment) in connections\n",
" already_visited = (next_city in view(path,1:hops))\n",
" if ! already_visited\n",
" path[next_hops] = next_city\n",
" visital_all_paths_recursive!(C_sorted,next_hops,path)\n",
" end\n",
" end\n",
" return nothing\n",
" else\n",
" @show path\n",
" return nothing\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "723a0f1a",
"metadata": {},
"outputs": [],
"source": [
"city = 1\n",
"visital_all_paths(C_sorted,city)"
]
},
{
"attachments": {
"g25091.png": {
"image/png": "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 truncated
}
},
"cell_type": "markdown",
"id": "8cbc8597",
"metadata": {},
"source": [
"<div>\n",
"<img src=\"attachment:g25091.png\" align=\"left\" width=\"525\"/>\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "65e654df",
"metadata": {},
"source": [
"### Serial implementation without pruning"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5989f0ac",
"metadata": {},
"outputs": [],
"source": [
"function tsp_serial_no_prune(C_sorted,city)\n",
" num_cities = length(C_sorted)\n",
" path=zeros(Int,num_cities)\n",
" hops = 1\n",
" path[hops] = city\n",
" distance=0\n",
" min_distance = typemax(Int)\n",
" tsp_serial_no_prune_recursive!(C_sorted,hops,path,distance,min_distance)\n",
"end\n",
"function tsp_serial_no_prune_recursive!(C_sorted,hops,path,distance,min_distance)\n",
" num_cities = length(C_sorted)\n",
" if hops != num_cities\n",
" city = path[hops]\n",
" connections = C_sorted[city]\n",
" next_hops = hops + 1\n",
" for (next_city,distance_increment) in connections\n",
" already_visited = (next_city in view(path,1:hops))\n",
" if ! already_visited\n",
" next_distance = distance + distance_increment\n",
" path[next_hops] = next_city\n",
" if next_distance < min_distance\n",
" min_distance = tsp_serial_recursive!(connections,next_hops,path,next_distance,min_distance)\n",
" end\n",
" min_distance = tsp_serial_no_prune_recursive!(\n",
" C_sorted,next_hops,path,next_distance,min_distance)\n",
" end\n",
" end \n",
" end\n",
" return min_distance\n",
"end\n",
"function visited(city,hops,path)\n",
" for i = 1:hops\n",
" if path[i] == city\n",
" return true\n",
" end\n",
" end\n",
" return false\n",
"end\n",
"\n",
"city = 1\n",
"tsp_serial(C,city)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "505e00f7",
"metadata": {},
"outputs": [],
"source": [
"function tsp_serial(C,city)\n",
" connections = sort_neighbors(C)\n",
" num_cities = length(connections)\n",
" current_route = (;path=zeros(Int,num_cities),distance=Ref(0))\n",
" min_route = (;path=zeros(Int,num_cities),distance=Ref(typemax(Int)))\n",
" hops = 1\n",
" current_route.path[hops] = city\n",
" tsp_serial_recursive!(connections,hops,current_route,min_route)\n",
" min_route\n",
"end\n",
"function tsp_serial_recursive(connections,hops,current_route,min_route)\n",
" num_cities = length(connections)\n",
" if hops == num_cities\n",
" if current_route.distance < min_route.distance\n",
" min_route.path .= current_route.path\n",
" min_route.distance[] = current_route.distance \n",
" return\n",
" end\n",
" return min_distance\n",
" else\n",
" min_distance = min(distance,min_distance)\n",
" #@show path, distance, min_distance\n",
" return min_distance\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d1be2bfc",
"metadata": {},
"outputs": [],
"source": [
"city = 1\n",
"min_distance = tsp_serial_no_prune(C_sorted,city)"
]
},
{
"cell_type": "markdown",
"id": "3c160cf9",
"metadata": {},
"source": [
"### Final serial implementation"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8241e0df",
"metadata": {},
"outputs": [],
"source": [
"function tsp_serial(C_sorted,city)\n",
" num_cities = length(C_sorted)\n",
" path=zeros(Int,num_cities)\n",
" hops = 1\n",
" path[hops] = city\n",
" distance=0\n",
" min_distance = typemax(Int)\n",
" tsp_serial_recursive!(C_sorted,hops,path,distance,min_distance)\n",
"end\n",
"function tsp_serial_recursive!(C_sorted,hops,path,distance,min_distance)\n",
" if distance >= min_distance\n",
" return min_distance\n",
" end\n",
" num_cities = length(C_sorted)\n",
" if hops != num_cities\n",
" city = path[hops]\n",
" connections = C_sorted[city]\n",
" next_hops = hops + 1\n",
" current_city = current_route.path[hops]\n",
" for (next_city,distance_increment) in connections[current_city]\n",
" if !visited(next_city,hops,current_route.path)\n",
" \n",
" next_distance = current_distance + distance_increment\n",
" \n",
" if next_distance < min_distance\n",
" return tsp_serial_recursive(connections,hops+1,current_route,min_route)\n",
" end\n",
" for (next_city,distance_increment) in connections\n",
" already_visited = (next_city in view(path,1:hops))\n",
" if ! already_visited\n",
" next_distance = distance + distance_increment\n",
" path[next_hops] = next_city\n",
" min_distance = tsp_serial_recursive!(\n",
" C_sorted,next_hops,path,next_distance,min_distance)\n",
" end\n",
" end \n",
" end\n",
" min_distance\n",
"end\n",
"\n",
"function visited(city,hops,path)\n",
" for i = 1:hops\n",
" if path[i] == city\n",
" return true\n",
" end\n",
" return min_distance\n",
" else\n",
" min_distance = min(distance,min_distance)\n",
" #@show path, distance, min_distance\n",
" return min_distance\n",
" end\n",
" return false\n",
"end\n",
"\n",
"city = 1\n",
"tsp_serial(C,city)"
]
},
{
"cell_type": "markdown",
"id": "94d8193f",
"metadata": {},
"source": [
"### Generating some test data\n",
"\n",
"The following function generates a random symmetric distance matrix, we have used a large. Note that the TSP problem at least requires that the underlying graph is connected (otherwise there is no route that visits all nodes). The next function does not guarantee this. In any case, we can still used a non-connected graph as input. The outcome of the search will be that there is no possible route."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "88fc4499",
"metadata": {},
"outputs": [],
"source": [
"function rand_symmetric_distance_table(n)\n",
" threshold = 0.2\n",
" mincost = 3\n",
" maxcost = 10\n",
" infinity = 10000*maxcost\n",
" C = fill(infinity,n,n)\n",
" for j in 1:n\n",
" for i in 1:j\n",
" if rand() > threshold\n",
" C[i,j] = rand(mincost:maxcost)\n",
" C[j,i] = C[i,j]\n",
" end\n",
" end\n",
" C[j,j] = 0\n",
" end\n",
" C,infinity\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d649cf80",
"id": "998087f2",
"metadata": {},
"outputs": [],
"source": [
"n = 10000\n",
"C, inf = rand_symmetric_distance_table(n)\n",
"city = 1\n",
"(;path,distance) = tsp_serial(C,city)\n",
"if distance >= inf\n",
" println(\"The input graph is nonconnected\")\n",
"else\n",
" (;path,distance)\n",
"end"
"min_distance = tsp_serial(C_sorted,city)"
]
},
{
"attachments": {
"g23360.png": {
"image/png": "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 truncated
}
},
"cell_type": "markdown",
"id": "b0ea4b99",
"id": "7f0a5d5c",
"metadata": {},
"source": [
"<div>\n",
"<img src=\"attachment:g23360.png\" align=\"left\" width=\"300\"/>\n",
"</div>"
"### Performance"
]
},
{
"attachments": {
"g22999.png": {
"image/png": "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 truncated
}
},
"cell_type": "markdown",
"id": "e734b6dc",
"metadata": {},
"source": [
"<div>\n",
"<img src=\"attachment:g22999.png\" align=\"left\" width=\"300\"/>\n",
"</div>"
]
},
{
"attachments": {
"g22822.png": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAwkAAAHMCAYAAACeIeSiAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAewgAAHsIBbtB1PgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7N132CRFtcfx77uBTeS8xF1yjkq4gARRUARFBQUzCAooioogKIoggl5EMIsBEERREUXgIiKgBFGCRMmZJW9gF5aNdf8477g9VT2xQ/XM/D7PMw/M7LzVZ2Y6ne46VUOIiMggWRJ4A7AJsD6wAbAisBQwHhgDzABmArOA+4EHgPuAG4AHyw9ZRERERETytj5wEnAzMB9wGR5PAucCbwNGlfkhREREREQkmzHAIcA/yJYUNHs8B5wBTC7pM4mIiIiISBfGA0cBT1FccuA/5mF3FzYo4fOJiEgJhmIHICIiuXk7cCawZhvvfQa4B6s3eAyrQ5gBLAQWH35MBNbDuittCIxs0eZ84CzgK1hNg4iIiIiIRLIycCmtr/b/Cfg4duLfqaWAfYDvYt2Mmi3rKWCvrj+NiIiIiIhksjvwLI1P2B8DPgOslOMyR2GFy5dhdx7SlrsQ+F9gdI7LFRERERGRFr4ALCD9JP0R4CMUf5K+JfD7BjE44EZghYJjEBEREREZeEPYVfq0k/K5WF3C4iXHtCtwb4OYHgbWKTkeEREREZGBMQI4j/ST8XuBTeOFxljg+ylxOeBpuquFEBERERGRFr5D+kn4+cCEiHEl7Y/N1uzH+CiwSsS4RERERET6zvGkJwinUL0hrbcFXiCM9Q5gyYhxiYiIiIj0jT1IL1L+XMygWtgAm4/Bj/nXMYMSEREREekHK5F+sn1yzKDatCkwlTD2j8cMSkRERESk111BeJJ9LtXrYtTIbthkbsn4ZwPrxgxKREQa65UDjIjIoNqfsHvOPcA2wKs5tL8qsDWwPjZyUs1TwAU5tF9zHPA177WrgDfnuAwRERERkb63BHay7l+B36jL9pYBdgSOAS4FphDeoag9/p4l8BQjgD+nLOfdOS9HRERERKSvfYHwpPqELtu6JKWtZo+8kwSAtbEkJ7mcB4GRBSxLRERERKTvjAOeIzyhHttle9cQP0kA+HLKst5b0LJERERERPrKJwhPpg/M0F6jJGEecCfwLOUkCROA571l3VHQskRERERE+sod1J9IPwSMytDeNcB8rOj5POBTWH3C+OF/v4xykgRInxRu2wKXJyIiIiLS8zYnPIk+LGOb67EoIUhTZpKwFPCKt7zvFbg8EREREZGe903qT6Bfw0YmKlKZSQLA+d7yXgRGF7xMERFp04jWbxERkZLt6T2/FJgWI5AC/cJ7vhzwuhiBiIhISEmCiEi1rAhs7L32hxiBFOxqYKb32m4xAhERkZCSBBGRatkFGPJeuyZCHEWbT9iladcYgYiISEhJgohItWzmPX8IeDpGICW41nvuf3YREYlESYKISLVs4D2/O0oU5bjHe74CsGyMQEREpJ6SBBGRalnPe/5AlCjKcX/Ka/7nFxGRCJQkiIhUy0Tv+SNRoijHY8BC7zX/84uISARKEkREqmUJ7/mMKFGUYwEwy3vN//wiIhKBkgQRkeoYDYzxXvOHCe03/udTkiAiUgFKEkREqiNtxuF5pUdRrjnecz9JEhGRCJQkiIhUx2zCPvoTYgRSoiW95/1+50REpCcoSRARqQ4HvOK91u/db/zPpyRBRKQClCSIiFTLdO/5SlGiKMfShN2L+rlQW0SkZyhJEBGpFn/I036eN2D9lNceKj0KEREJKEkQEamW+7znaSfS/cJPgOYBj8YIRERE6ilJEBGpFn8W4q2AxWIEUoJtvecPAfNjBCIiIvWUJIiIVMuN3vMJwOtjBFKCN3rPb4gShYiIBJQkiIhUy62ExbtvihFIwVYHNvBeuyZGICIiElKSICJSLfOBv3mvHRAjkIK913vuUJIgIlIZShJERKrnd97z9YDtYgRSoA94z28AnokRiIiIhJQkiIhUz9+xkX6SDosRSEF2BDb1Xvt1jEBERERERKpuU+BHwGys+03yMReYXOCyL/OW9/cCl3UF4ed7ETgVWKXA5YqIiIiI9IwdgUuBhYQnz8nHjzIsY0Xg4SaPV7xlzW7x/rW7jGNbmn/G14DzgA27bF9EREREpGeNBt4H3E7zk+bkYwGwTZfLW6WD5bTz8EcmascI4KY2218A/B5LoERERERE+toSwKeAx2l9onwH4d2Fm+munqwKScLHUtqZ1saybgU+CIzqYpkiIiIiIpW1MvAVYCqtr6BfyqLRjM5Pec8JXSw/dpKwDjb/Q7KNGcCqwN7A9W0s8xEswZrQ4bJFRERERCplMxoXIycfM4fft5739ysRXm1fAOxeQux5GQvcRviZj/DetzVWjzA/5b3Jx3TgTCzBEBERERHpGe0WIz+L3WFYtklbB6f83fOECUUVjcCGN/Xjvx4Y2eBv1saSAL+Y2n/MwZKKjYoLX0REREQkm9HAfsA/ad115gGs68zYNts+L6WNJ4DV8wu/EN8mjHsqsGYbf7sCcAwwJaWN5GMhcBXWbUlEREREpBJqxchP0Do5uB47mR3qYhn3pbR3PzAp6wcowAjgLNJrLvbqsK0xWOHyvSnt+Y/bUJGziIiIiETUaTHythmXtxbpV9WfBjbP2HaexgK/Iv27+GSGdkdgCdZVDdpWkbOIiIiIRLM51v1nLq2Lkc+kva417dqU9KRkNnZSHNuawD9I/z5OynE57RY5z0BFziIiIiJSoDyLkbPYBnihwbIvwPryl20IOIhwmNPa4xQ672LVjrXorMh54wJiEBEREZEBsxjWx/1OWndxuRM4lPaLkbPYAHisQRxTgcMor1/+lsANDWJZABxZQgzLY0XOTzeIQ0XOIiIiIpJZGcXIWU0ErmkS18PAIViiU4TXAZfQ+M7KS8A+BS27ERU5i4iIiEjuJmJdhfwJzBoVI28TJcpFRgInYvE0ivU5rEvO63JY3grAJ4CbmyzPATeRby1Gpzopcn4UuwuxVJRI+54bBW5lcJuC2w3c3uDeAG6t2JGJiIiItLIF7RUjv4ydcK8RJ8yGtgFuofUJ8RSsbuFQYAesm04jY7A+/O8EvgncSvNkpPb9HEW1rs5vhf2282gee63IebU4YfYLN2Y4ETgF3LXgZoFzDR4vgTsH3OtjRy0iIiKS1G4x8jPYHYZlokTZnpHA4VjhdKtkwT+xfxLronM71k3pRVqPHJR8zAd+AaxS+Kfs3mQsCZhF889SK3LeJE6Yvc6t0yQpaPRYCO6H4IrqGiciIiLSUq0Y+S5an/zeQXnFyHmpfb6H6CxZ6OYxFzuhXr+UT5aPpbB6k6do/fli1Zv0sNQkYRa4u8HdDO4WcE82SBYuBTci9icQERGRwbIkdnL4JINxcjgKeCvwS+BV8k0O/oWNWrRiaZ8mf7Vk6h5af97bh987OkqkPcWtA+4FcL8G91Fwazd43xrgTgM310sUDi83XhERERlUk4BTaV2MPBe4COjH/tGLA2/Bagv+BbxG+wnBQmy41QuAg7FuO/1kCNgd63bW6ruYgnU7WzpGoL3BjejsboB7O9bdqJYk3FdcbCIiIiKLipFbFazWipFXjxNmFCOwk/09gA8Dv6H+O5kK7It9h+PjhBjFlnRW5DxI60yB3OXe3YQq17aIiIhID+rkqnAvFCOX5Ujqv5sn4oYTXadFzpvGCbNfuOO8JGGr2BGJiIhIf6j1L7+b1snBv1H/cp+ShHS1OhYVORfKHeMlCRvEjkhERKQkbgK41cCtBG5M7Gj6iE7i8qEkoTkloYVyv0kkCNPBVWmeDRERkTy59cF9GhvS7ynCof4ewiYR2iV2pD1qElaMPJ3mJ2y1YuQ8ZhvuZ0oS2tNNdzYVOTfldgA3L7FvPCN2RCIiIgVwHwJ3W0pS0OxxLbh+GzGmKO0Wlg5iMXIWShI6p3UxE7csuM+CezmxL7wH3JKxIxMRESmAu7FJMjAX3PwG//Y8uI1jR19RGqKyeEoSujeJzu5q9eMQu02414O7KvG4HtwD4BZ4+8BLwC0fO1oREZGC1CUJ94E7EdyudtXsv++ZPHwF7QXvIPkwuF6a2bdonUx2pX7g2ShJyG7QJutrk9uzxZ3Ue8DtHDtKERGRgrkbsTG/d2rjvauBe9A7YB5VfIyVtxQqRi6bkoT81JLbu2i9/t4BHAr08cWBlkmCAzcTm315kObkEBGRweImdfj+11E/2+g/CgmrN3Q6Nv0mccLsS0oSirEjAz9nh1sO3O6Jx97gDgH3M2wko2Sy8G91ORIREfkvd0viIDk7djQRbEVns9yuFifMvqYkoVidzv69Rpwwy+aWAvcLL1H4MzjdGRQREUk5SC4RO6ISjMC6CV1F66usjwLHoGLkIilJKMeaWJHzNJqv87Ui523ihFkmNwTuIm8fuEfsqERERCrA/TFxcJzT51fRxtB+MfLtw+/VxErFU5JQLhU513GTvW6XP40dkYiISGRuFLhnEgfHO2JHVJBaMfLTND8hWojdXdg7TpgDS0lCHJ0UOd9JXxc5u0cS+8F/xo5GREQkMrefd5v9q7EjytlaWB/rV2h+AlQrRtZcEXEoSYivVuS8kObbyrNYkfOyqa30LHd7Yj94d+xoREREInITvKtnM8GtFDuqnGyNipF7iZKE6tgc23bm0nzbmYltO2vGCTNPbgjcc4l94TWxIxIREYnEDYH7pXcX4XOxo8qom2LkpaJEKj4lCdUzEbtj0KrIeQF2B2LbKFHmwm3n7Qt/EDsiERGRSNyxhMP+jYgdVZdqxcj30jo5uA0VI1eRkoTqWgKr53mC1ttXBYqcXYfbthsP7mZvf/iWYmITERGpNHcQ9SN5PNij3YyWx+4GTKH5iYuKkatPSUL1jQb2A/5J62ThASyxiFDk7O4F9ylwK7bx3s3B3eQlCP/q4QsmIiIi3XLvBjc/cUB8Line truncated
}
},
"cell_type": "markdown",
"id": "c8f723c4",
"metadata": {},
"source": [
"<div>\n",
"<img src=\"attachment:g22822.png\" align=\"left\" width=\"300\"/>\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "6f8100ad",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"id": "bef63110",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"id": "9f66ca90",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"id": "88b90b22",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "8850d90e",
"id": "e1eb74d8",
"metadata": {},
"outputs": [],
"source": [
"n = 11\n",
"using Random\n",
"Random.seed!(1)\n",
"C = rand(1:10,n,n)\n",
"C_sorted = sort_neighbors(C)\n",
"city = 1\n",
"@time tsp_serial_no_prune(C_sorted,city)\n",
"@time tsp_serial(C_sorted,city)"
]
},
{
"cell_type": "markdown",
"id": "733ce76e",
"metadata": {},
"source": [
"## Parallel implementation"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "354f5abb",
"metadata": {},
"outputs": [],
"source": [
@@ -536,137 +465,263 @@
{
"cell_type": "code",
"execution_count": null,
"id": "5d4935ee",
"id": "7f8f6702",
"metadata": {},
"outputs": [],
"source": [
"if procs() == workers()\n",
" addprocs(4)\n",
"if workers() == procs()\n",
" addprocs(3)\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4a2756ae",
"id": "5c0603a1",
"metadata": {},
"outputs": [],
"source": [
"@everywhere function visited(city,hops,path)\n",
" for i = 1:hops\n",
" if path[i] == city\n",
" return true\n",
"function enumerate_paths_dist(C_sorted,city,max_hops)\n",
" T = typeof((0,Int[]))\n",
" jobs_chnl = RemoteChannel(()->Channel{T}(1))\n",
" @sync begin\n",
" for w in workers()\n",
" @spawnat w consume_jobs(C_sorted,jobs_chnl)\n",
" end\n",
" generate_jobs(C_sorted,city,max_hops,jobs_chnl)\n",
" end\n",
" return false\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "78095098",
"metadata": {},
"outputs": [],
"source": [
"connections = [\n",
" [(1,0),(4,39),(5,76), (6,78),(3,94),(2,97)],\n",
" [(2,0),(5,25),(4,58),(3,62),(1,97),(6,109)],\n",
" [(3,0),(6,58),(2,62),(4,68),(5,70),(1,94)],\n",
" [(4,0),(5,38),(1,39),(2,58),(3,68),(6,78)],\n",
" [(5,0),(2,25),(4,38),(3,70),(1,76),(6,104)],\n",
" [(6,0),(3,58),(1,78),(4,78),(5,104),(2,109)]\n",
"]\n",
"city = 1\n",
"tsp_serial(connections,city)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "03f0dd8e",
"metadata": {},
"outputs": [],
"source": [
"@everywhere function tsp_dist_impl(connections,hops,path,current_distance,min_distance,max_hops,jobs_chnl,ftr_result)\n",
" num_cities = length(connections)\n",
" if hops == num_cities\n",
" if current_distance < min_distance\n",
" if ftr_result !== nothing\n",
" @spawnat 1 begin\n",
" result = fetch(ftr_result)\n",
" result.path .= path\n",
" result.min_distance_ref[] = current_distance\n",
" end |> wait\n",
" end\n",
" return current_distance\n",
" end\n",
" elseif hops <= max_hops\n",
" current_city = path[hops]\n",
" next_hops = hops + 1\n",
" for (next_city,distance_increment) in connections[current_city]\n",
" if !visited(next_city,hops,path)\n",
" path[next_hops] = next_city\n",
" next_distance = current_distance + distance_increment\n",
" if next_distance < min_distance\n",
" return tsp_dist_impl(connections,next_hops,path,next_distance,min_distance,max_hops,jobs_chnl,ftr_result)\n",
" end\n",
" end\n",
" end \n",
" else\n",
" if jobs_chnl !== nothing\n",
" put!(jobs_chnl,(;hops,path,current_distance))\n",
" end\n",
" end\n",
" min_distance\n",
"end\n",
"\n",
"function tsp_dist(connections,city)\n",
" max_hops = 2\n",
" num_cities = length(connections)\n",
"function generate_jobs(C_sorted,city,max_hops,jobs_chnl)\n",
" num_cities = length(C_sorted)\n",
" path=zeros(Int,num_cities)\n",
" hops = 1\n",
" path[hops] = city\n",
" current_distance = 0\n",
" min_distance = typemax(Int)\n",
" jobs_chnl = RemoteChannel(()->Channel{Any}(10))\n",
" ftr_result = @spawnat 1 (;path,min_distance_ref=Ref(min_distance))\n",
" task = @async begin\n",
" tsp_dist_impl(connections,hops,path,current_distance,min_distance,max_hops,jobs_chnl,nothing)\n",
" for w in workers()\n",
" put!(jobs_chnl,nothing)\n",
" end\n",
" generate_jobs_recursive(C_sorted,hops,path,max_hops,jobs_chnl)\n",
" for w in workers()\n",
" put!(jobs_chnl,(0,Int[]))\n",
" end\n",
" @sync for w in workers()\n",
" @spawnat w begin\n",
" max_hops = typemax(Int)\n",
" jobs_channel = nothing\n",
" while true\n",
" job = take!(jobs_chnl)\n",
" if job == nothing\n",
" break\n",
" end\n",
" hops = job.hops\n",
" path = job.path\n",
" current_distance = job.current_distance\n",
" tsp_dist_impl(connections,hops,path,current_distance,min_distance,max_hops,jobs_channel,ftr_result)\n",
" close(jobs_chnl)\n",
"end\n",
"function generate_jobs_recursive(C_sorted,hops,path,max_hops,jobs_chnl)\n",
" num_cities = length(C_sorted)\n",
" if hops == max_hops\n",
" @show path\n",
" put!(jobs_chnl,(hops,copy(path)))\n",
" return nothing\n",
" else\n",
" city = path[hops]\n",
" connections = C_sorted[city]\n",
" next_hops = hops + 1\n",
" for (next_city,distance_increment) in connections\n",
" already_visited = (next_city in view(path,1:hops))\n",
" if ! already_visited\n",
" path[next_hops] = next_city\n",
" generate_jobs_recursive(C_sorted,next_hops,path,max_hops,jobs_chnl)\n",
" end\n",
" end\n",
" end \n",
" result = fetch(ftr_result)\n",
" (;path = result.path, distance = result.min_distance_ref[])\n",
" return nothing\n",
" end\n",
"end\n",
"city = 1\n",
"tsp_dist(connections,city)"
"@everywhere function consume_jobs(C_sorted,jobs_chnl)\n",
" while true\n",
" hops,path = take!(jobs_chnl)\n",
" if hops == 0\n",
" println(\"Done!\")\n",
" break\n",
" end\n",
" consume_jobs_recursive(C_sorted,hops,path)\n",
" end\n",
"end\n",
"@everywhere function consume_jobs_recursive(C_sorted,hops,path)\n",
" num_cities = length(C_sorted)\n",
" if hops != num_cities\n",
" city = path[hops]\n",
" connections = C_sorted[city]\n",
" next_hops = hops + 1\n",
" for (next_city,distance_increment) in connections\n",
" already_visited = (next_city in view(path,1:hops))\n",
" if ! already_visited\n",
" path[next_hops] = next_city\n",
" consume_jobs_recursive(C_sorted,next_hops,path)\n",
" end\n",
" end\n",
" return nothing\n",
" else\n",
" @show path\n",
" return nothing\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "370a1205",
"id": "02801b66",
"metadata": {},
"outputs": [],
"source": []
"source": [
"C = [\n",
" 0 2 3 2\n",
" 2 0 4 1\n",
" 3 4 0 3\n",
" 2 1 3 0 \n",
"]\n",
"C_sorted = sort_neighbors(C)\n",
"city = 1\n",
"max_hops = 2\n",
"min_distance = enumerate_paths_dist(C_sorted,city,max_hops)"
]
},
{
"cell_type": "markdown",
"id": "0d579e3d",
"metadata": {},
"source": [
"### How to track the global minimum distance?"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d0303392",
"metadata": {},
"outputs": [],
"source": [
"buffer = 1 # very important\n",
"min_distance_chnl = RemoteChannel(()->Channel{Int}(buffer))\n",
"put!(min_distance_chnl,typemax(Int))\n",
"@sync for w in workers()\n",
" @spawnat w begin\n",
" sleep(rand(1:3))\n",
" min_distance = take!(min_distance_chnl)\n",
" @show min_distance\n",
" distance = rand(5:10)\n",
" min_distance = min(distance,min_distance)\n",
" @show distance\n",
" put!(min_distance_chnl,min_distance)\n",
" end\n",
"end\n",
"min_distance = take!(min_distance_chnl)\n",
"@show min_distance\n",
"close(min_distance_chnl)"
]
},
{
"cell_type": "markdown",
"id": "1fabefcb",
"metadata": {},
"source": [
"### Final parallel implementation"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "96c649b7",
"metadata": {},
"outputs": [],
"source": [
"function tsp_dist(C_sorted,city,max_hops)\n",
" T = typeof((0,Int[],0))\n",
" jobs_chnl = RemoteChannel(()->Channel{T}(1))\n",
" min_distance_chnl = RemoteChannel(()->Channel{Int}(1))\n",
" put!(min_distance_chnl,typemax(Int))\n",
" @sync begin\n",
" for w in workers()\n",
" @spawnat w consume_jobs(C_sorted,jobs_chnl,min_distance_chnl)\n",
" end\n",
" generate_jobs(C_sorted,city,max_hops,jobs_chnl)\n",
" end\n",
" min_distance = take!(min_distance_chnl)\n",
" close(min_distance_chnl)\n",
" return min_distance\n",
"end\n",
"function generate_jobs(C_sorted,city,max_hops,jobs_chnl)\n",
" num_cities = length(C_sorted)\n",
" path=zeros(Int,num_cities)\n",
" hops = 1\n",
" path[hops] = city\n",
" distance = 0\n",
" generate_jobs_recursive(C_sorted,hops,path,max_hops,jobs_chnl,distance)\n",
" for w in workers()\n",
" put!(jobs_chnl,(0,Int[],0))\n",
" end\n",
" close(jobs_chnl)\n",
"end\n",
"function generate_jobs_recursive(C_sorted,hops,path,max_hops,jobs_chnl,distance)\n",
" num_cities = length(C_sorted)\n",
" if hops == max_hops\n",
" @show path, distance\n",
" put!(jobs_chnl,(hops,copy(path),distance))\n",
" return nothing\n",
" else\n",
" city = path[hops]\n",
" connections = C_sorted[city]\n",
" next_hops = hops + 1\n",
" for (next_city,distance_increment) in connections\n",
" already_visited = (next_city in view(path,1:hops))\n",
" next_distance = distance + distance_increment\n",
" if ! already_visited\n",
" path[next_hops] = next_city\n",
" generate_jobs_recursive(\n",
" C_sorted,next_hops,path,max_hops,jobs_chnl,next_distance)\n",
" end\n",
" end\n",
" return nothing\n",
" end\n",
"end\n",
"@everywhere function consume_jobs(C_sorted,jobs_chnl,min_distance_chnl)\n",
" while true\n",
" hops,path,distance = take!(jobs_chnl)\n",
" if hops == 0\n",
" println(\"Done!\")\n",
" break\n",
" end\n",
" min_distance = take!(min_distance_chnl)\n",
" put!(min_distance_chnl,min_distance)\n",
" consume_jobs_recursive(C_sorted,hops,path,min_distance_chnl,distance,min_distance)\n",
" end\n",
"end\n",
"@everywhere function consume_jobs_recursive(C_sorted,hops,path,min_distance_chnl,distance,min_distance)\n",
" if distance >= min_distance\n",
" return min_distance\n",
" end\n",
" num_cities = length(C_sorted)\n",
" if hops != num_cities\n",
" city = path[hops]\n",
" connections = C_sorted[city]\n",
" next_hops = hops + 1\n",
" for (next_city,distance_increment) in connections\n",
" already_visited = (next_city in view(path,1:hops))\n",
" next_distance = distance + distance_increment\n",
" if ! already_visited\n",
" path[next_hops] = next_city\n",
" min_distance = consume_jobs_recursive(\n",
" C_sorted,next_hops,path,min_distance_chnl,next_distance,min_distance)\n",
" end\n",
" end\n",
" return min_distance\n",
" else\n",
" min_distance = take!(min_distance_chnl)\n",
" min_distance = min(min_distance,distance)\n",
" put!(min_distance_chnl,min_distance)\n",
" @show path, distance, min_distance\n",
" return min_distance\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "67888155",
"metadata": {},
"outputs": [],
"source": [
"city = 1\n",
"max_hops = 2\n",
"min_distance = tsp_dist(C_sorted,city,max_hops)"
]
}
],
"metadata": {