@@ 1,6 1,6 @@
# Intro
-This is report for a hw01 for EOA course on CTU FEE. The goal
+This is a report for hw01 for EOA course on CTU FEE. The goal
has been to try to solve traveling salesperson problem by
means of evolutionary algorithms.
@@ 10,7 10,7 @@ exactly once, returning to the origin city.
The report covers the implemented
algorithms and the results on 10 TSP instances from TSPLIB. All of
-those chosen instances are using euclidian metric and are 2D.
+those chosen instances are using euclidean metric and are 2D.
{latex-placement="ht"}
@@ 18,11 18,11 @@ those chosen instances are using euclidian metric and are 2D.
Three generic algorithms have been used for solving the TSP.
Then various representations and operators have been tried on them.
-## Evolution algorithm
+## Evolutionary algorithm
The algorithm keeps a population of N each epoch and generates offsprings
out of the current population. Specifically it uses the following steps:
-1. Obtain initial population (ie. random or specialized heuristic).
+1. Obtain initial population (i.e., random or specialized heuristic).
2. Select parents to make offsprings from (selection).
3. Pair the selected parents (pairing).
4. Somehow join the parent pairs to make an offspring (crossover).
@@ 40,7 40,7 @@ where the current best solution is being perturbated, until a better solution is
And then, the process starts over from this better solution.
The local search implementation supports evolutionary strategies,
-ie. changing the perturbation paramters during the run, but none
+i.e., changing the perturbation parameters during the run, but none
were used for TSP.
The implementation is available in `eoa_lib/src/local_search/mod.rs`.
@@ 49,18 49,18 @@ The implementation is available in `eoa_lib/src/local_search/mod.rs`.
Random search initializes new random elements in the search space each
iteration and saves new elements if they are better than best found.
-The implementation is available in `eoa_lib/src/random_search.rs`
+The implementation is available in `eoa_lib/src/random_search.rs`.
# Representations
-Two representation have been chosen for the TSP problem,
+Two representations have been chosen for the TSP problem,
1. Node permutation
2. Binary string half-matrix denoting if city i comes before city j
## Node permutation
Implemented as a vector of indices of the cities.
-Couple of perturbations and crossovers have been implemented:
+A couple of perturbations and crossovers have been implemented:
- Perturbations
- Move single random city to random position
@@ 117,7 117,7 @@ Edge recombination is the most complicated from the three crossover operators.
First, an adjacency list is created for both parents. Let's take an example
of a permutation: 012345.
-The element 0 has 5 and 1 as adjacement nodes. 1 has 0 and 2 and so on.
+The element 0 has 5 and 1 as adjacent nodes. 1 has 0 and 2 and so on.
The two adjacency lists are then joined together, while omitting repetitions.
That means that a single element will have from 2 to 4 elements in its adjacency list.
@@ 126,7 126,7 @@ To make an offspring:
1. First element is taken out of the first parent, this is the current element (city).
2. The current element is appended to offspring
3. The current element is removed from adjacency lists of all other elements.
-4. Then, all the cities in adjacency list for current element are taken and one with minimum neighbors in its adjacency list is taken. If there are mutliple such cities, random one is chosen.
+4. Then, all the cities in adjacency list for current element are taken and one with minimum neighbors in its adjacency list is taken. If there are multiple such cities, random one is chosen.
5. The found city becomes current element.
6. Steps 2 to 5 are repeated until all elements are taken.
@@ 137,7 137,7 @@ a vector of 4 elements is made beforehand. Each element may be unset (an Option)
Then, the adjacencies from first parent are put to positions 0, 1 and adjacencies from
second parent to positions 2 and 3. If there would be a repetition, it's left unset.
This then allows for static allocation only. This is thanks to a library called nalgebra
-is used and it's possible to tell it the dimensions of adjacency matrix
+that is used and it's possible to tell it the dimensions of adjacency matrix
beforehand. However for the runs of the algorithms, `Dyn` dimension has been used
out of convenience, so dynamic allocation is performed.
@@ 180,7 180,7 @@ Instead of starting with random solutions, two heuristics have been tried to
make the initial populations for the evolutionary algorithms.
## Nearest neighbors
-The heuristic starts at a given node and finds it's nearest neighbor. Then
+The heuristic starts at a given node and finds its nearest neighbor. Then
adds that neighbor to the permutation and moves to it. Then it repeats search
for the nearest neighbor, making sure to not select nodes twice. The whole
chromosome is built like this.
@@ 210,42 210,47 @@ To initialize the whole population, the same algorithm is ran multiple times. Si
it is random, its results should be different, at least slightly.
# Results
-To compare all the algoritms on various instances, always 10 runs of the algorithm
-have been made on the given instance. All the graphs of probabilities of reaching
+To compare all the algorithms on various instances, 10 runs of each algorithm
+have been performed on each instance. All the graphs of probabilities of reaching
target for given function evaluation were then constructed from
-averaging between the runs and between the instances. The fitness charts sometimes show less runs to not be too
-overwhelming. All evolution algorithms ran on 5000 iterations (epochs) and the local search
+averaging between the runs and between the instances. The fitness charts sometimes show fewer runs to not be too
+overwhelming. All evolutionary algorithms ran for 10000 iterations (epochs) and the local search
and random search were adjusted to run on the same number of fitness function evaluations.
-The population size for the evaluation algorithm 500 has been chosen.
-The number of parents (and thus offpsrings) is 250.
+The population size for the evolutionary algorithm is 500.
+The number of parents (and thus offspring) is 250.
+
+On the x-axis of all graphs is the number of evaluations of the fitness function.
+
+The graphs with probability of reaching targets have been computed as follows:
+
+1. Individually computed graphs for targets 1 %, 5 %, 10 % and 20 %
+2. Averaged the graphs
+
+To compute the individual graphs, at each evaluation point, we count how many runs have already achieved better results than the target and divide this by the total number of runs.
+
+Then, standard deviation has been added, clamped between 0 and 1.
The instances chosen from TSPLIB, all 2D Euclidean:
- eil51
- eil76
-- eil101
- kroA100
+- eil101
+- pr124
- ch150
- kroA150
- kroA200
+- ts225
- a280
-- u574
-- u724
## Comparing algorithms
To compare the algorithms,
first it has been ensured the algorithms were tweaked to produce the best results (best that the author
-has been capable of). Then, they were ran on 10 instances of TSP and averaged in the following chart:
+has been capable of). Then, they were run on 10 instances of TSP and averaged in the following chart:
-TODO Here should be a graph with fitness on eil76
-- EA
-- LS
-- RS
+
-TODO Here should be a graph with probability of success (all instances averaged)
-- EA
-- LS
-- RS
+
## Comparing perturbations on LS
Four perturbations have been evaluated:
@@ 254,17 259,9 @@ Four perturbations have been evaluated:
- Reversing subsequence
- Combination of the above (specifically single random perturbation has been chosen to run)
-TODO Here should be a graph with fitness on eil76
-- LS (move)
-- LS (swap)
-- LS (reverse)
-- LS (mix)
+
-TODO Here should be a graph with probability of success(all instances averaged)
-- LS (move)
-- LS (swap)
-- LS (reverse)
-- LS (mix)
+
from the experiment it seems the reversal of a subsequence is the best singular perturbation,
but the combination is even better. That could be explained by the fact that in cases the subsequence
@@ 281,18 278,13 @@ then allows for comparison between the crossovers themselves.
From the runs, the edge recombination has produced the best results, with partially
mapped crossover being the second and cycle crossover being the worst.
-TODO Here should be a graph with probability of success(all instances averaged)
-- EA (pmx)
-- EA (erx)
-- EA (cx)
+
## Comparing representations
-TODO Here should be a graph with probability of success(all instances averaged)
-- EA (binary)
-- EA (normal)
-- LS (binary)
-- LS (normal)
+
+
+
On one hand the binary string representation is simpler, because it doesn't require special treatment
for crossovers. Any crossover leads to valid result. On the other hand since it's not so specific to the
@@ 303,13 295,7 @@ subsequence perturbation is making it that much better, as can be seen from comp
Both of the heuristics are capable of making initial solutions much better
than random ones. Here is a fitness graph with only the two heuristics.
-TODO Here should be a graph with probability of success(all instances averaged)
-- EA (normal)
-- EA (nn)
-- EA (mst)
-- LS (normal)
-- LS (nn)
-- EA (mst)
+
From the results it can be seen the nearest neighbor heuristic performs better.
But this could be caused by the fact that minimal spanning tree has been used
@@ 330,21 316,21 @@ Rust has been chosen as the language. There are three subdirectories, `eoa_lib`,
`eoa_lib` is the library with the generic operators defined, with random search, local search and evolution
algorithm functions. It also contains the most common representations, perturbations and crossovers for them.
-`tsp_hw01` contains the TSP implementation and runs all of the algorithms. It then produces csv results
+`tsp_hw01` contains the TSP implementation and runs all of the algorithms. It then produces CSV results
with best candidate evaluations for given fitness function evaluation count. This is then utilized by
-`tsp_plotter`. The node permutation tsp representation itself is implemented in `tsp.rs`. The configurations
+`tsp_plotter`. The node permutation TSP representation itself is implemented in `tsp.rs`. The configurations
of all algorithms used are located in `main.rs`. All the instances used are located in `tsp_hw01/instances` and
-the solutions are put to `tsp_hw01/solutions`
+the solutions are put to `tsp_hw01/solutions`.
`tsp_plotter` contains hard-coded presets for charts to create for the report.
# Usage of LLM
While I was working on this homework, I have used LLM for writing certain parts of the code,
-specifically I have used it very extensively for the tasks that I do not like to do much myself:
+specifically I have used it very extensively for routine tasks:
- Loading data,
- Plotting graphs,
-- Refactoring of a triviality at a lot of places at once (ie. at first I chose to make perturbation copy the chromosome,
+- Refactoring of a triviality at a lot of places at once (i.e., at first I chose to make perturbation copy the chromosome,
but I realized this is very ineffective for EAs afterwards and changed it to change in-place instead),
- Writing some of the tests
@@ 354,8 340,8 @@ task.
I have used LLM only minimally for implementing the algorithms or for deciding on how
to make the implementation, mainly sometimes in the form of checking if the algorithm
-looks correct. (and it did find a few issues that I then sometimes let it fix and
-sometimes fix myself). This is because I believe that I can learn the most by
+looks correct. It did find a few issues that I then sometimes let it fix and
+sometimes fixed myself. This is because I believe that I can learn the most by
writing the implementations myself.
I use Claude from within claude-code, a CLI tool that is capable