Skip to content

Commit a2198ae

Browse files
committed
first draft of the ants TP
1 parent 2b21c10 commit a2198ae

40 files changed

+1518
-0
lines changed

notebooks/myst-toc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ project:
7272
- file: tps/metro/README-metro-nb.md
7373
- pattern: tps/pagerank-thrones/README*-nb.md
7474
- pattern: tps/puzzle8/README-*-nb.md
75+
- file: tps/ants/README-ants-nb.md
7576

7677
- title: TPs visu & games
7778
children:
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
---
2+
jupytext:
3+
formats: md:myst
4+
text_representation:
5+
extension: .md
6+
format_name: myst
7+
kernelspec:
8+
display_name: Python 3 (ipykernel)
9+
language: python
10+
name: python3
11+
language_info:
12+
name: python
13+
nbconvert_exporter: python
14+
pygments_lexer: ipython3
15+
---
16+
17+
# ants: a genetic algorithm
18+
19+
+++ {"tags": ["prune-cell"]}
20+
21+
````{admonition} nothing to prune
22+
:class: warning
23+
24+
there are no difference - apart from this very cell - between the teacher and the student version, but the notebook is duplicated in .teacher for consistency
25+
````
26+
27+
+++ {"slideshow": {"slide_type": ""}, "tags": ["prune-cell"]}
28+
29+
```{admonition} grab the zip for starters
30+
31+
{download}`you will need the zipfile that you can find here<./ARTEFACTS-ants.zip>`
32+
```
33+
34+
+++
35+
36+
## problem statement
37+
38+
our goal in this activity is to implement an optimization algorithm that is an example of a *genetic algorithm*
39+
it turns out this algorithm can solve two equivalent problems
40+
41+
### traveling salesman
42+
43+
specifically, we consider a famous problem, which is of great importance for everything logistics, and is known as **the *"traveling salesman"* problem**
44+
consider for example a truck that needs to deliver parcels in several locations
45+
the problem is to find **the shortest path** (or more accurately, a reasonably short path)
46+
47+
- that starts from the warehouse
48+
- goes through all the locations
49+
- and returns to the warehouse
50+
51+
### ants colony problem
52+
53+
the traveling salesman problem is in fact equivalent to the one known as the **the *"ants colony"* problem**, where the colony needs to find the best route from the ant hill and back, that goes through the spots where food has been found
54+
55+
this latter metaphor is **a little more helpful** though in the context of the genetic algorithm, because the algorithm indeed mimicks the existence of several ants that concurrently walk the graph, and leave pheromons to keep track of the past
56+
57+
```{admonition} not *the* shortest path
58+
is has been shown that finding *the* shortest path is an NP-problem, so it's untractable
59+
but as explained in the video, the genetic algorithm finds reasonable paths, in that they do not differ much from the optimal one
60+
```
61+
62+
+++
63+
64+
## ACO: a super useful resource
65+
66+
the YouTube video below gives a 20' introduction on the algorithm known as *Ants Colony Optimization (ACO)*, and explains essentially all the logic and formulas needed to implement it
67+
```{iframe} https://www.youtube.com/embed/u7bQomllcJw?rel=0&amp;controls=1
68+
```
69+
so it is a **highly recommended** resource to get started with the details of the algorithm
70+
71+
```{admonition} not a video fan ?
72+
73+
[I found this one helpful too, in a totally different style](https://perso.liris.cnrs.fr/christine.solnon/publications/cpaior.pdf)
74+
75+
and there's plenty others around as well, google for 'ACO algorithm'
76+
```
77+
78+
+++
79+
80+
## useful data
81+
82+
in the zip file you will find:
83+
84+
+++
85+
86+
### `data/*.csv`
87+
88+
we provide a few datafiles in the `data/` folder, made from some graphs that appear in the video
89+
these use a simple csv format that should be self explanatory (just forget the `radius` column)
90+
91+
| name | timestamp in video | # nodes |
92+
|-|-|-|
93+
| data/video-50.csv | 1:17 | 50 |
94+
| data/video-30.csv | 3:14 | 30 |
95+
| data/video-04.csv | 8:57 | 4 |
96+
| data/video-10.csv | 11:25 | 10 |
97+
| data/video-06.csv | 14:54 | 6 |
98+
| data/video-66.csv | 17:44 | 66 |
99+
100+
plus for convenience some polygon-like shapes in `poly*.csv`
101+
102+
+++
103+
104+
### `data/*.path`
105+
106+
here we provide the results found by our own implementation; this in particular is used by the 'Cheat' button in `ui.py` - see below
107+
108+
+++
109+
110+
## problem.py
111+
112+
also provided in the zip, you can use `problem.py` like so:
113+
114+
```{code-cell} ipython3
115+
from problem import Problem2D
116+
117+
problem = Problem2D("data/video-06.csv")
118+
```
119+
120+
```{code-cell} ipython3
121+
# how many nodes
122+
len(problem)
123+
```
124+
125+
```{code-cell} ipython3
126+
# to iterate over nodes
127+
128+
for node in problem:
129+
print(node)
130+
```
131+
132+
```{code-cell} ipython3
133+
# or rather
134+
135+
for index, node in enumerate(problem):
136+
print(f"{index}-th node is {node}")
137+
```
138+
139+
```{code-cell} ipython3
140+
# get distance between 2 nodes
141+
142+
problem.distance(0, 2)
143+
```
144+
145+
```{code-cell} ipython3
146+
# what it says
147+
148+
problem.distance_along_path([0, 1, 2, 3, 0])
149+
```
150+
151+
#### displaying the graphs
152+
153+
+++
154+
155+
we've also coded some display featured for your convenience
156+
157+
1st off, **provided that you have `graphviz` installed**
158+
159+
```{code-cell} ipython3
160+
# you can do display it with graphviz like this
161+
162+
problem.to_graphviz()
163+
```
164+
165+
```{code-cell} ipython3
166+
# or this if you prefer
167+
168+
problem.to_graphviz(show_distances=False)
169+
```
170+
171+
```{code-cell} ipython3
172+
# prune-cell
173+
# sub-optimal for big graphs though
174+
Problem2D("data/video-50.csv").to_graphviz(show_distances=False)
175+
```
176+
177+
#### if you prefer plotly
178+
179+
and there again, **provided that you have `plotly` installed** you could do
180+
181+
```{code-cell} ipython3
182+
import plotly.io as pio
183+
184+
# Try one of these depending on your setup:
185+
pio.renderers.default = "notebook" # for classic Jupyter Notebook
186+
```
187+
188+
```{code-cell} ipython3
189+
problem.to_plotly()
190+
```
191+
192+
```{code-cell} ipython3
193+
# prune-cell
194+
195+
Problem2D("data/video-50.csv").to_plotly(show_distances=False)
196+
```
197+
198+
```{code-cell} ipython3
199+
# prune-begin
200+
201+
p6 = Problem2D("data/video-06.csv")
202+
p6.print_distances()
203+
```
204+
205+
## `ui.py`
206+
207+
might come in handy too, it is a simple `flet` UI that lets you visualize your work; you might consider
208+
209+
- reading its code
210+
- make sure your `solver.py` code fits the expected interface - or tweak `ui.py` according to your own interface
211+
- run it with
212+
```bash
213+
flet run -d ui.py
214+
```
215+
so that the UI will hot reload upon any change you make in `solver.py`

notebooks/tps/ants/.teacher/data

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../data/
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"""
2+
do a gridsearch on the solver meta-parameters
3+
"""
4+
5+
import math
6+
from itertools import product
7+
8+
from problem import Problem2D
9+
from solver import Solver
10+
11+
ITERATIONS = 10
12+
13+
# ALPHAS = [0.1, 0.5, 1, 2, 5]
14+
ALPHAS = [1]
15+
# BETAS = [0.1, 0.5, 1, 2, 5]
16+
BETAS = [1]
17+
# VAPORIZATIONS = [0.1, 0.5, 0.9]
18+
VAPORIZATIONS = [0.64]
19+
# PHERO_INITS = [0.1, 0.5, 1, 2, 5]
20+
PHERO_INITS = [1]
21+
22+
def use_default_parameters():
23+
global ALPHAS, BETAS, VAPORIZATIONS, PHERO_INITS
24+
from solver import ALPHA, BETA, VAPORISATION, PHERO_INIT
25+
ALPHAS = [ALPHA]
26+
BETAS = [BETA]
27+
VAPORIZATIONS = [VAPORISATION]
28+
PHERO_INITS = [PHERO_INIT]
29+
30+
31+
def gridsearch(filename, iterations):
32+
p = Problem2D(filename)
33+
best_distance = math.inf
34+
best_path = None
35+
best_params = None
36+
37+
CUBE = (ALPHAS, BETAS, VAPORIZATIONS, PHERO_INITS)
38+
nb_combinations = len(ALPHAS) * len(BETAS) * len(VAPORIZATIONS) * len(PHERO_INITS)
39+
40+
for index, hyper_params in enumerate(product(*CUBE), 1):
41+
(alpha, beta, vaporization, phero_init) = hyper_params
42+
s = Solver(p, alpha=alpha, beta=beta, vaporisation=vaporization,
43+
phero_init=phero_init)
44+
path, distance = s.solve(iterations)
45+
print(
46+
f"{index}/{nb_combinations} "
47+
f"alpha={alpha} beta={beta} vaporization={vaporization} phero_init={phero_init}"
48+
f"path={path} distance={distance}")
49+
if distance < best_distance:
50+
best_distance = distance
51+
best_path = path
52+
best_params = hyper_params
53+
54+
print(f"best path={best_path} distance={best_distance} with params={best_params}")
55+
# save in .path file
56+
# compute max euclidian diameter
57+
max_diameter = max(p.distance(i, j)
58+
for i, j in product(range(len(p)), repeat=2) if i != j)
59+
for i in range(len(p)):
60+
for j in range(i+1, len(p)):
61+
d = p.distance(i, j)
62+
if d > max_diameter:
63+
max_diameter = d
64+
cheated_filename = filename.replace(".csv", ".path")
65+
with open(cheated_filename, "w") as f:
66+
import json
67+
print(f"saving best path to {cheated_filename}")
68+
json.dump(dict(
69+
path=best_path,
70+
distance=best_distance,
71+
params=best_params,
72+
iterations=iterations,
73+
diameter=max_diameter,
74+
), f)
75+
76+
from argparse import ArgumentParser
77+
def main():
78+
parser = ArgumentParser()
79+
parser.add_argument("filenames", nargs="+",help="the problem filename (csv)")
80+
parser.add_argument(
81+
"-i", "--iterations", type=int, default=ITERATIONS,
82+
help=f"number of iterations per run (default: {ITERATIONS})")
83+
# useful only to recompute the .path files
84+
parser.add_argument(
85+
"-d", "--default-parameters", action="store_true",
86+
help="use the default parameters from solver.py instead of the gridsearch")
87+
88+
args = parser.parse_args()
89+
if args.default_parameters:
90+
use_default_parameters()
91+
for filename in args.filenames:
92+
gridsearch(filename, args.iterations)
93+
94+
if __name__ == "__main__":
95+
main()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from problem import Problem2D
2+
3+
from solver import Solver
4+
5+
def demo_solver():
6+
p = Problem2D("data/video-10.csv")
7+
s = Solver(p)
8+
path, distance = s.solve(20)
9+
print("found path=", path, "distance=", p.distance_along_path(path))
10+
11+
demo_solver()

notebooks/tps/ants/.teacher/media

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../media/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../problem.py

0 commit comments

Comments
 (0)