add mo tutorial doc in Markdown format
The original tutorial link[http://paradiseo.gforge.inria.fr/index.php?n=Doc.Tutorials] is broken. I found the archive from here: https://web.archive.org/web/20210119160107/http://paradiseo.gforge.inria.fr/index.php?n=Doc.Tutorials
This commit is contained in:
parent
3b7ffbbfae
commit
1f1f598314
9 changed files with 1123 additions and 0 deletions
448
mo/tutorial/Lesson1/README.md
Normal file
448
mo/tutorial/Lesson1/README.md
Normal file
|
|
@ -0,0 +1,448 @@
|
||||||
|
# How to implement your first hill-climber algorithm?
|
||||||
|
This lesson will let you
|
||||||
|
* Run your first simple Hill-Climber within MO library
|
||||||
|
* Learn the main principles of MO
|
||||||
|
* Browse through the code of these algorithm
|
||||||
|
* Run other kind of Hill-Climbers: first-improvment, random best, neutral hill-climbers.
|
||||||
|
* Design your own evaluation functions
|
||||||
|
* Add your own stopping criteria
|
||||||
|
|
||||||
|
## 1. I want to run my first hill-climber!
|
||||||
|
If you followed the the Install instructions, all examples would be compiled. If it is not the case, refer to the above instructions.
|
||||||
|
|
||||||
|
You can run your hill-climber. From the "build/mo/tutorial/Lesson1" directory, type:
|
||||||
|
```shell
|
||||||
|
./lesson1_simpleHC -V=20
|
||||||
|
```
|
||||||
|
Great! A very simple hill-climber had solved the oneMax problem (which maximizes the number of ones in the bit string) for the bit strings of size 20. On your output screen, you can see on the first line the random initial solution, and the second line, the final solution which is (I hope) the bit string of size 20 with all ones. For example:
|
||||||
|
```text
|
||||||
|
initial: 6 20 00010000011000100101
|
||||||
|
final: 20 20 11111111111111111111
|
||||||
|
```
|
||||||
|
The first number is the fitness of the solution, the second the size of the bit string and following all the bits of the solution.
|
||||||
|
|
||||||
|
## 2. The main principles of MO
|
||||||
|
In MO a local search is defined by this schema:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
At each iteration, from the current solution:
|
||||||
|
* Generate neighbors from the neighborhood and evaluate them
|
||||||
|
* Select a neighbor
|
||||||
|
* Decide to replace solution by the selected neighbor
|
||||||
|
|
||||||
|
Like in paradisEO-eo, MO separates the representation-dependent part (represention of solution, neighbor, evaluation) from the generic part (the order of neighbors generation, selection, replacement, etc.).
|
||||||
|
|
||||||
|
So, to define a local search, it is necessary to define:
|
||||||
|
* the solution representation,
|
||||||
|
* the neighbor representation,
|
||||||
|
* the evaluation function of a solution,
|
||||||
|
* the evaluation function of a neighbor,
|
||||||
|
* the neighborhood (generation of the neighbors)
|
||||||
|
|
||||||
|
If you can define the representation-dependent part of your problem, you can use all local search algorithms from MO (Hill-climbing, simulated annealing, tabu search, iterated local search, variable neighborhood search, and more) and even combined them with evolutionary algorithms !
|
||||||
|
|
||||||
|
Of course a lot of classical representations, neighborhoods and problems are already defined in MO. They will be explained all along this tutorial.
|
||||||
|
Let's first have a look on the oneMax problem solved by a simple Hill-Climber algorithm defined as follows:
|
||||||
|
```text
|
||||||
|
Choose randomly initial solution x
|
||||||
|
Do
|
||||||
|
best <- first neighbor of x
|
||||||
|
For all y in the neighborhood of x
|
||||||
|
If (f(y) > f(best))
|
||||||
|
best <- y
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
If (f(x) < f(best))
|
||||||
|
x <- best
|
||||||
|
continue <- true
|
||||||
|
else
|
||||||
|
continue <- false
|
||||||
|
endif
|
||||||
|
While continue == true
|
||||||
|
```
|
||||||
|
|
||||||
|
The simple HC stops on local optima when no improvement can not be made.
|
||||||
|
|
||||||
|
## 3. Browsing the code
|
||||||
|
|
||||||
|
Now let's have a look on the code. All the elements of the simple HC are already defined in the MO framework, and the code only puts all this bricks together. The code following the previous general principles. It defines the solution and neighbor representation, the solution and neighbor evaluation, the neighborhood, and the local search used.
|
||||||
|
Please, open the file "mo/tutorial/Lesson1/lesson1_simpleHC.cpp", and follow me in the code:
|
||||||
|
|
||||||
|
### 1. The includes part:
|
||||||
|
|
||||||
|
The general includes for the c++ stdlib streams:
|
||||||
|
```c++
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
This includes for eo which contains all include files of EO:
|
||||||
|
```c++
|
||||||
|
#include <eo>
|
||||||
|
```
|
||||||
|
|
||||||
|
The first line to include the bit string representation defined in eo, and the second one to include the bit string neighbor representation. All classical problem-dependent part of MO are defined in the sub-directory "problems". How to define your representation is explained in EO tutorial, and how to design your neighbor will be explain in the next lesson 2. Here just use it.
|
||||||
|
```c++
|
||||||
|
#include <ga/eoBit.h>
|
||||||
|
#include <problems/bitString/moBitNeighbor.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
This includes the evaluation function of a solution (full evaluation), the incremental evaluation of a neighbor for the oneMax problem, and a possible evaluation of neighbor using the full evaluation. You will learn at the end how to define your evaluations.
|
||||||
|
```c++
|
||||||
|
#include <problems/eval/oneMaxFullEval.h>
|
||||||
|
#include <problems/eval/moOneMaxIncrEval.h>
|
||||||
|
#include <eval/moFullEvalByModif.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
This neighborhood visits all bit string at Hamming distance 1 in increasing order of bit index from bit 0 to bit vecSize-1. All the neighborhoods are included in the "neighborhood" directory.
|
||||||
|
```c++
|
||||||
|
#include <neighborhood/moOrderNeighborhood.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can include the simple hill-climbing local search. All local search algorithms are included in the "algo" directory.
|
||||||
|
```c++
|
||||||
|
#include <algo/moSimpleHC.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. The typedef part:
|
||||||
|
|
||||||
|
EO can apply an evolutionary algorithm on any type of solution. So, all EO classes are parametrized by the type of solutions, and it is useful to use a synonym (with a typedef) of the solution's type.
|
||||||
|
MO can apply an local search algorithm on any type of solution and neighbor. So, for the same reason, all classes of MO are parametrized by the neighbor's type. In the neighbor class, the solution's type is defined. More precision on the neighbor design will be given in the lesson 2.
|
||||||
|
Here the solution representation is a bit string and the neighbor representation is related to a bit string solution and Hamming distance 1 (only 1 bit can be flipped), both using an "unsigned int" fitness value.
|
||||||
|
```c++
|
||||||
|
typedef eoBit<unsigned int> Indi;
|
||||||
|
typedef moBitNeighbor<unsigned int> Neighbor;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Object definition part:
|
||||||
|
|
||||||
|
Follows the main function "main_function" where all useful objects are defined.\\
|
||||||
|
First, a code to parse the command line and a file. It gives the value of the random seed and the size of bit string. The lesson 3 of EO tutorial gives more precision on this code. Here we have only to understand that the variables "seed" and "vecSize" are initialized.
|
||||||
|
```c++
|
||||||
|
eoParser parser(argc, argv);
|
||||||
|
|
||||||
|
eoValueParam<uint32_t> seedParam(time(0), "seed", "Random number seed", 'S');
|
||||||
|
parser.processParam( seedParam );
|
||||||
|
unsigned seed = seedParam.value();
|
||||||
|
|
||||||
|
// length of the bit string
|
||||||
|
eoValueParam<unsigned int> vecSizeParam(8, "vecSize", "Genotype size", 'V');
|
||||||
|
parser.processParam( vecSizeParam, "Representation" );
|
||||||
|
unsigned vecSize = vecSizeParam.value();
|
||||||
|
(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
To seed the random seed (see lesson 1 of EO tutorial for more precision):
|
||||||
|
```c++
|
||||||
|
rng.reseed(seed);
|
||||||
|
```
|
||||||
|
|
||||||
|
The definition the initialization of solutions is not defined is MO but in EO. The "eoInitFixedLength" is a class that makes a random intialization of bit string of a given length. Each bit is true with 1/2 rate. You can see the lesson 1 of EO tutorial lesson 1 for more precision.
|
||||||
|
```c++
|
||||||
|
eoUniformGenerator<bool> uGen;
|
||||||
|
eoInitFixedLength<Indi> random(vecSize, uGen);
|
||||||
|
```
|
||||||
|
|
||||||
|
The fitness function of the oneMax problem is the number of 1 in the bit string. It is already defined in MO:
|
||||||
|
```c++
|
||||||
|
oneMaxFullEval<Indi> fullEval;
|
||||||
|
```
|
||||||
|
|
||||||
|
A neighbor is not necessary a modified copy of the solution. But often a neighbor defines how to move, i.e. how to modify a solution to compute the neighbor. For example, for the bit string, a neighbor indicates which bit is flipped.
|
||||||
|
In the same way, it is often possible to define the incremental evaluation of a neighbor knowing the modification (the move).
|
||||||
|
The incremental evaluation for the oneMax problem is already defined and adds +1 or -1 at the fitness value according to the flipped bit.
|
||||||
|
```c++
|
||||||
|
moOneMaxIncrEval<Neighbor> neighborEval;
|
||||||
|
```
|
||||||
|
|
||||||
|
When the incremental evaluation can not be defined. The neighbor can be evaluated with the full evaluation. First the solution is modified on the neighbor, than the full evaluation is computed on it and then the solution is move back. This is done by the class "moFullEvalByModif". If you want you to test it comment the line with moOneMaxIncrEval and uncomment the line with moFullEvalByCopy:
|
||||||
|
```c++
|
||||||
|
// moFullEvalByModif<Neighbor> neighborEval(fullEval);
|
||||||
|
```
|
||||||
|
|
||||||
|
For the simple hill-climbing, all the neighbors are explored in increasing order of their bit flip. So, you can use the class "moOrderNeighborhood" where the size of the neighborhood has to be precise in the constructor:
|
||||||
|
```c++
|
||||||
|
moOrderNeighborhood<Neighbor> neighborhood(vecSize);
|
||||||
|
```
|
||||||
|
|
||||||
|
All representation-dependent part is now defined, so the simple Hill-Climbing can be defined. The constructor needs the neighborhood, the solution and neighbor evaluations. The solution and neighbor representation are defined by the template of the class:
|
||||||
|
```c++
|
||||||
|
moSimpleHC<Neighbor> hc(neighborhood, fullEval, neighborEval);
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. The execution of hill-climbing part:
|
||||||
|
|
||||||
|
Always in the "main_function" function, it is the execution part.
|
||||||
|
In MO, the local search algorithm never initializes the solution. It must be made outside the local search algorithm. This allows to combine local search algorithms with evolutionary algorithms or with others local search algorithms.
|
||||||
|
Now apply your local search on the solution as follows:
|
||||||
|
```c++
|
||||||
|
// The current solution
|
||||||
|
Indi solution;
|
||||||
|
|
||||||
|
// random initialization
|
||||||
|
random(solution);
|
||||||
|
|
||||||
|
// Evaluation of the intial solution:
|
||||||
|
// can be evaluated here, or else it will be done at the beginning of the local search
|
||||||
|
fullEval(solution);
|
||||||
|
|
||||||
|
// output: the initial solution
|
||||||
|
std::cout << "initial: " << solution << std::endl ;
|
||||||
|
|
||||||
|
// apply the local search on the solution !
|
||||||
|
hc(solution);
|
||||||
|
|
||||||
|
// output: the final solution
|
||||||
|
std::cout << "final: " << solution << std::endl ;
|
||||||
|
```
|
||||||
|
|
||||||
|
The main line is "hc(solution)" which apply the local search on the solution.
|
||||||
|
Easy, isn't it?
|
||||||
|
|
||||||
|
## 4. Others hill-climbing algorithms
|
||||||
|
|
||||||
|
You may think that the simple hill-climbing is not the hill-climbing you need because you don't want to explore all the neighborhood or because there is several solutions with the same fitness value.
|
||||||
|
|
||||||
|
You may want to implement the first improvement hill-climbing defined as follows:
|
||||||
|
```text
|
||||||
|
Choose randomly initial solution x
|
||||||
|
Do
|
||||||
|
y <- not visited random neighbor of x
|
||||||
|
If (f(x) < f(y))
|
||||||
|
x <- y
|
||||||
|
else
|
||||||
|
y.visited <- true
|
||||||
|
endif
|
||||||
|
While number of non visited neighbor of x > 0
|
||||||
|
```
|
||||||
|
|
||||||
|
In the first improvement HC, if the fitness of a random neighbor is higher than the current fitness, the neighbor is selected. And this hill-climber stops on local optima when no more fitness improvement can be made in the neighborhood.
|
||||||
|
|
||||||
|
To implement the first improvement HC, you have to change only 2 lines in the previous code.
|
||||||
|
The one to change the neighborhood generation:
|
||||||
|
```c++
|
||||||
|
moRndWithoutReplNeighborhood<Neighbor> neighborhood(vecSize);
|
||||||
|
```
|
||||||
|
In this neighborhood, the neighbors are generated in an randomly order (not from 0 to vecSize-1) without replacement which means that the neighbors are generated only once.
|
||||||
|
|
||||||
|
And of course, the other one to change the local search algorithm:
|
||||||
|
```c++
|
||||||
|
moFirstImprHC<Neighbor> hc(neighborhood, fullEval, neighborEval);
|
||||||
|
```
|
||||||
|
|
||||||
|
The corresponding include files have to be changed too. See and run the code "mo/tutorial/Lesson1/lesson1_firstImprHC.cpp".
|
||||||
|
|
||||||
|
For a hill-climbing which takes care on the solutions with equal fitness value such as this one:
|
||||||
|
```text
|
||||||
|
Choose randomly initial solution x
|
||||||
|
Do
|
||||||
|
bestVect <- [ first neighbor of x ]
|
||||||
|
For all y in the neighborhood of x
|
||||||
|
If (f(y) > f(bestVect.first))
|
||||||
|
bestVect <- [ y ]
|
||||||
|
else if (f(y) == f(bestVect.first) and y != bestVect.first)
|
||||||
|
bestVect.push_back(y)
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
If (f(x) < f(bestVect.first))
|
||||||
|
x <- choose randomly a solution from bestVect
|
||||||
|
continue <- true
|
||||||
|
else
|
||||||
|
continue <- false
|
||||||
|
endif
|
||||||
|
While continue == true
|
||||||
|
```
|
||||||
|
|
||||||
|
You only have to change the local search algorithm by (see the code in mo/tutorial/Lesson1/lesson1_randomBestHC.cpp) :
|
||||||
|
```c++
|
||||||
|
moRandomBestHC<Neighbor> hc(neighborhood, fullEval, neighborEval);
|
||||||
|
```
|
||||||
|
|
||||||
|
Easy, isn't it?
|
||||||
|
|
||||||
|
And if you don't want that your hill-climber stops if there is some solution with the same fitness in the neighborhood like this:
|
||||||
|
```text
|
||||||
|
nbStep <- 0
|
||||||
|
Choose randomly initial solution x
|
||||||
|
Do
|
||||||
|
bestVect <- [ first neighbor of x ]
|
||||||
|
For all y in the neighborhood of x
|
||||||
|
If (f(y) > f(bestVect.first))
|
||||||
|
bestVect <- [ y ]
|
||||||
|
else if (f(y) == f(bestVect.first))
|
||||||
|
bestVect.push_back(y)
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
If (f(x) <= f(bestVect.first))
|
||||||
|
x <- choose randomly a solution from bestVect
|
||||||
|
continue <- true
|
||||||
|
else
|
||||||
|
continue <- false
|
||||||
|
endif
|
||||||
|
nbStep <- nbStep + 1
|
||||||
|
While continue == true and nbStep > nbStepMax
|
||||||
|
```
|
||||||
|
|
||||||
|
This hill-climber stops on strict local optima, but not plateau which are local optima. So, another parameter is need to stop the algorithm (nbStepMax). Then you only have to change the local search algorithm by (see the code in Lesson1/lesson1_neutralHC.cpp) :
|
||||||
|
```c++
|
||||||
|
moNeutralHC<Neighbor> hc(neighborhood, fullEval, neighborEval, nbStepMax);
|
||||||
|
```
|
||||||
|
|
||||||
|
Easy, isn't it?
|
||||||
|
|
||||||
|
## 5. Define your evaluation
|
||||||
|
|
||||||
|
### 1. Evaluation of solutions:
|
||||||
|
You can learn to define your fitness function in the EO tutorial lesson 1. But let's me shortly explain how to do.
|
||||||
|
You have to define a class inherited from the "eoEvalFunc<EOT>". For example, the oneMaxFullEval is defined as follows:
|
||||||
|
```c++
|
||||||
|
#include <eoEvalFunc.h>
|
||||||
|
template< class EOT >
|
||||||
|
class oneMaxFullEval : public eoEvalFunc<EOT>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of 1 in a bitString
|
||||||
|
* @param _sol the solution to evaluate
|
||||||
|
*/
|
||||||
|
void operator() (EOT& _solution) {
|
||||||
|
unsigned int sum = 0;
|
||||||
|
for (unsigned int i = 0; i < _solution.size(); i++)
|
||||||
|
sum += _solution[i];
|
||||||
|
_solution.fitness(sum);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The file "eoEvalFunc.h" must be included at the begining. The class "oneMaxFullEval" inherits from the "eoEvalFunc" class. Like all class in EO, the classes are templatized by the solution type "EOT".
|
||||||
|
EO uses a functor style: the fitness function is computed in the method "operator()(EOT& _sol)". Do what you want to compute the fitness value in this method, and puts the fitness value in the solution at the end by using its method "fitness":
|
||||||
|
```c++
|
||||||
|
_solution.fitness( fitnessValue );
|
||||||
|
```
|
||||||
|
|
||||||
|
The "eoBit" class is vector of boolean. The size of the vector is obtained by the method "size()":
|
||||||
|
```c++
|
||||||
|
_solution.size()
|
||||||
|
```
|
||||||
|
and the value of the ith bit is given by the classical method "operator[]":
|
||||||
|
```c++
|
||||||
|
_solution[i]
|
||||||
|
```
|
||||||
|
The fitness value of a solution can be obtained with the method "fitness()":
|
||||||
|
```c++
|
||||||
|
_solution.fitness()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Evaluation of neighbors:
|
||||||
|
MO uses the same idea to evaluate a neighbor. You have to define a class which inherits from "moEval" class. For example, the oneMaxIncrEval is defined as follows:
|
||||||
|
```c++
|
||||||
|
#include <eval/moEval.h>
|
||||||
|
template< class Neighbor >
|
||||||
|
class moOneMaxIncrEval : public moEval<Neighbor>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef typename Neighbor::EOT EOT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* incremental evaluation of the neighbor for the oneMax problem
|
||||||
|
* @param _solution the solution to move (bit string)
|
||||||
|
* @param _neighbor the neighbor to consider (of type moBitNeigbor)
|
||||||
|
*/
|
||||||
|
virtual void operator()(EOT & _solution, Neighbor & _neighbor) {
|
||||||
|
if (_solution[_neighbor.index()] == 0)
|
||||||
|
_neighbor.fitness(_solution.fitness() + 1);
|
||||||
|
else
|
||||||
|
_neighbor.fitness(_solution.fitness() - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The file "moEval.h" must be included at the begining. All class to define evalutation function are in the "eval" directory of MO. The class "oneMaxIncrEval" inherits from the "moEval" class. Like all class in MO, the classes are templatized by the neighbor's type "Neighbor".
|
||||||
|
A typedef is defined to easily have access to the solution type "EOT". The solution type is the type "EOT" in the class of Neighbor:
|
||||||
|
```c++
|
||||||
|
typedef typename Neighbor::EOT EOT;
|
||||||
|
```
|
||||||
|
|
||||||
|
MO also uses a functor style: the evaluation function is computed in the method "operator()(EOT& _solution, Neighbor & _neighbor)" which depends on the current solution and its neighbor to consider. Do what you want to compute the fitness value of the neighbor in this method, and puts the fitness value in the neighbor by using its method "fitness":
|
||||||
|
```c++
|
||||||
|
_neighbor.fitness( fitnessValue );
|
||||||
|
```
|
||||||
|
|
||||||
|
The "moBitNeighbor" has a method "index()" which gives the number of flipped bit. When the flipped bit of the solution is set to "0" the number of 1 in the neighbor is increased by one, and decreased by one otherwise.
|
||||||
|
When it is possible the incremental evaluation of neighbor gives a better complexity. For example the full evaluation needs vecSize comparisons, and the incremental evaluation only one comparison.
|
||||||
|
This prototypical example helps you to define your own solution and neighbor evaluations !
|
||||||
|
|
||||||
|
### 6. Use your stopping criteria
|
||||||
|
|
||||||
|
All local search algorithms have their own stopping criteria, but it is possible to add other stopping criteria. The predefined stopping criteria you can add are:
|
||||||
|
* stop on the number of iterations
|
||||||
|
* stop on the number of full evaluation
|
||||||
|
* stop on the number of neighbor evaluation
|
||||||
|
* stop on the fitness value reached
|
||||||
|
* stop on a time limit
|
||||||
|
|
||||||
|
All criteria inherit from the class "moContinuator" in the directory "continuator". To use one of them, you have to include the correct file, to define your object from the class and add the continuator to the local search constructor.
|
||||||
|
For example, to add a maximum number of iteration to your simple hill-climber. First include the correct file:
|
||||||
|
```c++
|
||||||
|
#include <continuator/moIterContinuator.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
Define an object from the class:
|
||||||
|
```c++
|
||||||
|
moIterContinuator<Neighbor> continuator(iterMax);
|
||||||
|
```
|
||||||
|
|
||||||
|
And add the continuator in the constructor of the HC:
|
||||||
|
```c++
|
||||||
|
moSimpleHC<Neighbor> hc(neighborhood, fullEval, neighborEval, continuator);
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples on all continuators are given in the source codes "lesson1_iterContinuator.cpp", "lesson1_fitContinuator.cpp", "lesson1_fullevalContinuator.cpp", "lesson1_evalContinuator.cpp".
|
||||||
|
|
||||||
|
It is also possible to combine several continuators with the class "moCombinedContinuator.h" as follows:
|
||||||
|
```c++
|
||||||
|
moIterContinuator<Neighbor> iterCont(iterMax);
|
||||||
|
moFitContinuator<Neighbor> fitCont(fitnessMax);
|
||||||
|
moFullEvalContinuator<Neighbor> fullevalCont(fullEval, fullevalMax);
|
||||||
|
moNeighborEvalContinuator<Neighbor> evalCont(neighborEval, evalMax);
|
||||||
|
|
||||||
|
moCombinedContinuator<Neighbor> continuator(iterCont);
|
||||||
|
continuator.add(fitCont);
|
||||||
|
continuator.add(fullevalCont);
|
||||||
|
continuator.add(evalCont);
|
||||||
|
```
|
||||||
|
|
||||||
|
The local search stops when one of the continuators is false.
|
||||||
|
Easy, isn't it?
|
||||||
|
|
||||||
|
Of course, you can define your own continuator. You must inherit your own class from the "moContinuator" class. Then 2 methods must be defined:
|
||||||
|
* the test method:
|
||||||
|
```c++
|
||||||
|
virtual bool operator()(EOT & _solution)
|
||||||
|
```
|
||||||
|
|
||||||
|
The returned value is true when the local search can continue for the next iteration, and false otherwise.
|
||||||
|
|
||||||
|
* the intialisation method:
|
||||||
|
```c++
|
||||||
|
virtual void init(EOT & _solution)
|
||||||
|
```
|
||||||
|
Something you want to initialize before starting the local search (for example a counter).
|
||||||
|
The predefined continuator can help you to define your own continuator. Have fun now!
|
||||||
|
|
||||||
|
## 7. Exercices
|
||||||
|
|
||||||
|
1. Define the evaluation function and the incremental evaluation of the Royal Road problem. And run a first-improvement hill-climber on it!
|
||||||
|
|
||||||
|
The Royal Road is defined in this paper:
|
||||||
|
"[The Royal Road for Genetic Algorithms: Fitness Landscapes and GA Performance](http://web.cecs.pdx.edu/~mm/handbook-of-ec-rr.pdf)" Mitchell, Forerest, Holland.
|
||||||
|
|
||||||
|
2. Define the evaluation function and the incremental evaluation of the MAX-SAT problem. And run a first-improvement hill-climber on it!
|
||||||
BIN
mo/tutorial/Lesson1/schemaLS.jpg
Executable file
BIN
mo/tutorial/Lesson1/schemaLS.jpg
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
124
mo/tutorial/Lesson2/README.md
Normal file
124
mo/tutorial/Lesson2/README.md
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
# How to implement and use neighborhoods
|
||||||
|
In this lesson, you will learn how to implement a neighbor, neighborhood and the evaluation function. Two ways will be show, one generic and one using an indexed neighborhoods. As an example, it will be illustrated on the Queens problem.
|
||||||
|
|
||||||
|
1. Classical neighborhoods (example with a swap operator)
|
||||||
|
2. Indexed neighbordhoods (example with a shift operator)
|
||||||
|
3. Evaluation of neighbors
|
||||||
|
4. Exercise
|
||||||
|
|
||||||
|
## 1. Classical neighborhoods (example with a swap operator)
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
To implement a neighborhood for your problem, you must have a class that inherits from "moNeighborhood" and a class that inherits from "moNeighbor" for the corresponding neighbors. As a consequence, in the neighborhood class, you have to implement the following methods:
|
||||||
|
|
||||||
|
hasNeighbor (test if there is at least one valid neighbor)
|
||||||
|
init (init the first neighbor)
|
||||||
|
cont (test if there is again a valid neighbor)
|
||||||
|
next (compute the next valid neighbor)
|
||||||
|
And in the neighbor class:
|
||||||
|
|
||||||
|
move (how to apply the move corresponding to the neighbor on a solution)
|
||||||
|
### Example
|
||||||
|
In the "paradiseo-mo/src/problems/permutation" directory, classical neighborhood and neighbor for swap operator (moSwapNeighborhood.h and moSwapNeighbor.h) are defined. Some methods are specific to the swap operator and you can see a "move_back" methods that is explained at the end of this tutorial.
|
||||||
|
|
||||||
|
In "mo/tutorial/Lesson2" directory, open the source file "testNeighborhood.cpp". You can see how to use this first neighborhood...
|
||||||
|
|
||||||
|
After inclusion, useful types are defined for more lisibility:
|
||||||
|
|
||||||
|
Define type of representation
|
||||||
|
```c++
|
||||||
|
typedef eoInt<unsigned int> Queen;
|
||||||
|
```
|
||||||
|
Define type of a swap neighbor
|
||||||
|
```c++
|
||||||
|
typedef moSwapNeighbor<Queen> swapNeighbor;
|
||||||
|
```
|
||||||
|
Define type of the swap neighborhood
|
||||||
|
```c++
|
||||||
|
typedef moSwapNeighborhood<Queen> swapNeighborhood;
|
||||||
|
```
|
||||||
|
And in the "main" fonction, a neighborhood, a solution and a neighbor are declared:
|
||||||
|
```c++
|
||||||
|
swapNeighborhood swapNH;
|
||||||
|
Queen solution;
|
||||||
|
swapNeighbor n1;
|
||||||
|
```
|
||||||
|
|
||||||
|
Then they are used to explore and print all the neighbors of the neighborhood for a Queen problem of size 8 (swapEval is the evaluation function declared previously)
|
||||||
|
```c++
|
||||||
|
swapNH.init(solution, n1);
|
||||||
|
swapEval(solution,n1);
|
||||||
|
n1.print();
|
||||||
|
while(swapNH.cont(solution)){
|
||||||
|
swapNH.next(solution, n1);
|
||||||
|
swapEval(solution,n1);
|
||||||
|
n1.print();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can run the executable on the lesson 2 directory and see the output (the beginning).
|
||||||
|
|
||||||
|
## 2. Indexed neighbordhoods (example with a shift operator)
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
Three indexed neighborhoods are already defined in Paradiseo-MO. To use them you have to know the size of your neighborhoods and define a mapping that associates a neighbor from a known key, in your class neighbor. This neighbor must inherit from "moIndexNeighbor".
|
||||||
|
|
||||||
|
### Example
|
||||||
|
In the mo/src/problems/permutation" directory, a neighbor for shift operator (moShiftNeighbor.h) is defined. In this class, the mapping is done in the method "translate".
|
||||||
|
|
||||||
|
After inclusion useful types are defined for more lisibility:
|
||||||
|
|
||||||
|
Define type of a shift neighbor
|
||||||
|
```c++
|
||||||
|
typedef moShiftNeighbor<Queen> shiftNeighbor;
|
||||||
|
```
|
||||||
|
Define three different indexed neighborhoods for shift operator
|
||||||
|
```c++
|
||||||
|
typedef moOrderNeighborhood<shiftNeighbor> orderShiftNeighborhood;
|
||||||
|
typedef moRndWithoutReplNeighborhood<shiftNeighbor> rndWithoutReplShiftNeighborhood;
|
||||||
|
typedef moRndWithReplNeighborhood<shiftNeighbor> rndWithReplShiftNeighborhood;
|
||||||
|
```
|
||||||
|
|
||||||
|
And in the "main" fonction, a shift neighbor and the three indexed neighborhoods are declared:
|
||||||
|
```c++
|
||||||
|
shiftNeighbor n2;
|
||||||
|
orderShiftNeighborhood orderShiftNH(pow(vecSize-1, 2));
|
||||||
|
rndWithoutReplShiftNeighborhood rndNoReplShiftNH(pow(vecSize-1, 2));
|
||||||
|
rndWithReplShiftNeighborhood rndReplShiftNH(pow(vecSize-1, 2));
|
||||||
|
```
|
||||||
|
|
||||||
|
Exploration of the neighborhoods is done like with a classical neighborhood.
|
||||||
|
|
||||||
|
You can run the executable on the lesson 2 directory and see the output.
|
||||||
|
|
||||||
|
## 3. Evaluation of neighbors
|
||||||
|
|
||||||
|
There are three ways to evaluate a neighbor:
|
||||||
|
|
||||||
|
1. Incremental evaluation
|
||||||
|
2. Full evaluation by modification
|
||||||
|
3. Full evaluation by copy
|
||||||
|
|
||||||
|
In terms of performance, it is more efficient to use incremental evaluation and if it cannot be defined, full evaluation by modification is better than that one by copy.
|
||||||
|
|
||||||
|
### Incremental evaluation
|
||||||
|
To implement an incremental evaluation, you have to create a class which inherits of "**moEval**". So you have to define the method:
|
||||||
|
```c++
|
||||||
|
void operator()(EOT&, Neighbor&){ ... }
|
||||||
|
```
|
||||||
|
EOT and Neighbor are respectively the templates for a solution and a neighbor.
|
||||||
|
|
||||||
|
### Full evaluation
|
||||||
|
The two full evaluations are already defined in Paradiseo-MO. The full evaluation by modification applies the move on the initial solution, evaluates the obtained solution and affects the fitness value to the neighbor. Then the "moveBack" is applied to come back to the initial solution. On the other hand, the full evaluation by copy applies the move on a temporary copy of the solution, evaluates it and affects the fitness value to the neighbor.
|
||||||
|
|
||||||
|
To use these evaluations, you need your classical full evaluation function ("eoEvalFunc") in the constructors:
|
||||||
|
```c++
|
||||||
|
moFullEvalByCopy(eoEvalFunc<EOT>& _eval)
|
||||||
|
moFullEvalByModif(eoEvalFunc<EOT>& _eval)
|
||||||
|
```
|
||||||
|
|
||||||
|
Be carefull, if you want to use the class "moFullEvalByModif", your neighbor must be "backable" and so it has to inherit of the class "**moBackableNeighbor**" and consequently to have a method "moveBack".
|
||||||
|
|
||||||
|
## 4. Exercise
|
||||||
|
|
||||||
|
Try to define an indexed swap neighbor like in the file "moShiftNeighbor.h". Then explore and print the neighborhood randomly.
|
||||||
110
mo/tutorial/Lesson3/README.md
Normal file
110
mo/tutorial/Lesson3/README.md
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
# Lesson3 - How to use Simulated Annealing and Checkpointing
|
||||||
|
In this lesson, a simple simulated annealing is presented, using an order neighborhood based on a shift operator, to solve the Queen problem. Then, a checkpoint will be used to save some informations during the search.
|
||||||
|
|
||||||
|
1. Simulating Annealing on the Queen problem.
|
||||||
|
2. Checkpointing
|
||||||
|
3. Avalaible statistics in MO
|
||||||
|
4. Exercise
|
||||||
|
|
||||||
|
## 1. Simulating Annealing (example on the Queen problem)
|
||||||
|
|
||||||
|
First you have to define the representation of a Queen, how to initialize and evaluate it. So you have to declare three classes:
|
||||||
|
```c++
|
||||||
|
queenFullEval<Queen> fullEval;
|
||||||
|
eoInitPermutation<Queen> init(vecSize);
|
||||||
|
Queen solution1;
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, you have to ramdomly intialize and evaluate the solution:
|
||||||
|
```c++
|
||||||
|
init(solution1);
|
||||||
|
fullEval(solution1);
|
||||||
|
```
|
||||||
|
|
||||||
|
Let see the most simple constructor of a Simulated Annealing (in algo/moSA.h). You need three parameters:
|
||||||
|
* a neighborhood
|
||||||
|
* a full evaluation function (declared before)
|
||||||
|
* a neighbor's evaluation function
|
||||||
|
```c++
|
||||||
|
moFullEvalByCopy<shiftNeighbor> shiftEval(fullEval);
|
||||||
|
rndShiftNeighborhood rndShiftNH(pow(vecSize-1, 2));
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now declare the Simulated Annealing:
|
||||||
|
```c++
|
||||||
|
moSA<shiftNeighbor> localSearch1(rndShiftNH, fullEval, shiftEval);
|
||||||
|
```
|
||||||
|
This simple constructor uses by default three components:
|
||||||
|
* moSimpleCoolingSchedule (with default parameters)
|
||||||
|
* moSolNeighborComparator
|
||||||
|
* moTrueContinuator
|
||||||
|
|
||||||
|
More flexible constructors exist in which you can change these components. In the following, the "moTrueContinuator" is replaced by a "moCheckpoint".
|
||||||
|
|
||||||
|
You can try this first algorithm with different problem sizes (use parameter file or the option --vecSize=X on command line to execute "testSimulatedAnnealing"). It prints the initial and final solution1.
|
||||||
|
|
||||||
|
## 2. Checkpointing (example on the Queen problem)
|
||||||
|
|
||||||
|
The class "moCheckpoint" inherits of the abstract class "moContinuator" and allows to incorporate one or many "moContinuator" classes (Composite pattern). It also allows to incorporate many "eoMonitor", "eoUpdater" and "moStatBase" classes.
|
||||||
|
|
||||||
|
Here, an example of checkpointing is presented, including:
|
||||||
|
* a continuator returning always true (moTrueContinuator)
|
||||||
|
* a monitor saving information in a file (eoFileMonitor)
|
||||||
|
* an updater using the file monitor with a determinated frequency (moCounterMonitorSaver)
|
||||||
|
* a very simple statistical operator giving only the fitness of the current solution (moFitnessStat)
|
||||||
|
|
||||||
|
First, you have to define the "moTrueContinuator" and build the "moCheckpoint":
|
||||||
|
```c++
|
||||||
|
moTrueContinuator<shiftNeighbor> continuator;
|
||||||
|
moCheckpoint<shiftNeighbor> checkpoint(continuator);
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, create the "moFitnessStat" and add it in the checkpoint:
|
||||||
|
```c++
|
||||||
|
moFitnessStat<Queen> fitStat;
|
||||||
|
checkpoint.add(fitStat);
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, create the "eoFileMonitor" to write fitness values in the file fitness.out and the "moCounterMonitorSaver" to use the file monitor only for each 100 iterations.
|
||||||
|
```c++
|
||||||
|
eoFileMonitor monitor("fitness.out", "");
|
||||||
|
moCounterMonitorSaver countMon(100, monitor);
|
||||||
|
checkpoint.add(countMon);
|
||||||
|
monitor.add(fitStat);
|
||||||
|
```
|
||||||
|
|
||||||
|
So you can create a Simulated Annealing with this checkpoint:
|
||||||
|
```c++
|
||||||
|
moSA<shiftNeighbor> localSearch2(rndShiftNH, fullEval, shiftEval, coolingSchedule, solComparator, checkpoint);
|
||||||
|
```
|
||||||
|
|
||||||
|
Try this second algorithm with different problem sizes (use parameter file or the option --vecSize=X on command line to execute "testSimulatedAnnealing"). It prints the initial and final solution2 and you can see the evolution of fitness values in the file fitness.out (only 1 value each 100 iterations).
|
||||||
|
|
||||||
|
## 3. Avalaible statistics
|
||||||
|
|
||||||
|
A lot of statistics are avalaible to have informations during the search:
|
||||||
|
|
||||||
|
* moCounterStat
|
||||||
|
* moMinusOneCounterStat
|
||||||
|
* moStatFromStat
|
||||||
|
* moFitnessStat
|
||||||
|
* moNbInfNeighborStat
|
||||||
|
* moNbSupNeighborStat
|
||||||
|
* moNeutralDegreeNeighborStat
|
||||||
|
* moSizeNeighborStat
|
||||||
|
* moNeighborhoodStat
|
||||||
|
* moDistanceStat
|
||||||
|
* moSolutionStat
|
||||||
|
* moBestSoFarStat
|
||||||
|
* moSecondMomentNeighborStat
|
||||||
|
* moMaxNeighborStat
|
||||||
|
* moMinNeighborStat
|
||||||
|
* moNeighborBestStat
|
||||||
|
* moNeighborFitnessStat
|
||||||
|
* moAverageFitnessNeighborStat
|
||||||
|
* moStdFitnessNeighborStat
|
||||||
|
|
||||||
|
## 4. Exercise
|
||||||
|
|
||||||
|
1. Try to add the cooling schedule parameters into the parameters file. Then, try the simulated annealing with different parameters to see theirs impacts on the search.
|
||||||
|
2. Add an existed operator (in continuator directory) to print the solution each 100 iterations.
|
||||||
67
mo/tutorial/Lesson4/README.md
Normal file
67
mo/tutorial/Lesson4/README.md
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
# How to use Tabu Search
|
||||||
|
In this lesson, a simple tabu search is presented, using an order neighborhood based on a shift operator, to solve the Queen problem.
|
||||||
|
1. Tabu Search on the Queen problem.
|
||||||
|
2. Exercise
|
||||||
|
|
||||||
|
## 1. Tabu Search (example on the Queen problem)
|
||||||
|
|
||||||
|
First you have to define the representation of a Queen, how to initialize and how to evaluate it. So you have to declare three classes:
|
||||||
|
```c++
|
||||||
|
queenFullEval<Queen> fullEval;
|
||||||
|
eoInitPermutation<Queen> init(vecSize);
|
||||||
|
Queen sol1;
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, you have to ramdomly intialize a solution:
|
||||||
|
```c++
|
||||||
|
init(sol1);
|
||||||
|
fullEval(sol1);
|
||||||
|
```
|
||||||
|
|
||||||
|
Let see the most simple constructor of a Tabu Search (in mo/src/algo/moTS.h). You need five parameters:
|
||||||
|
|
||||||
|
* a neighborhood
|
||||||
|
```c++
|
||||||
|
orderShiftNeighborhood orderShiftNH(pow(vecSize-1, 2));
|
||||||
|
```
|
||||||
|
* a full evaluation function (declared before)
|
||||||
|
* a neighbor evaluation function*
|
||||||
|
```c++
|
||||||
|
moFullEvalByCopy<shiftNeighbor> shiftEval(fullEval);
|
||||||
|
```
|
||||||
|
* a time limit for the search (in seconds)
|
||||||
|
* a size for the tabu list
|
||||||
|
|
||||||
|
You can now declare the Tabu Search:
|
||||||
|
```c++
|
||||||
|
moTS<shiftNeighbor> localSearch1(orderShiftNH, fullEval, shiftEval, 2, 7);
|
||||||
|
// 2 is the time limit, 7 is the size of the tabu List
|
||||||
|
```
|
||||||
|
|
||||||
|
This simple constructor uses by default seven components:
|
||||||
|
* moTimeContinuator
|
||||||
|
* moNeighborComparator
|
||||||
|
* moSolNeighborComparator
|
||||||
|
* moNeighborVectorTabuList
|
||||||
|
* moDummyIntensification
|
||||||
|
* moDummyDiversification
|
||||||
|
* moBestImprAspiration
|
||||||
|
|
||||||
|
More flexible constructors exist as you can change these components:
|
||||||
|
```c++
|
||||||
|
moNeighborVectorTabuList<shiftNeighbor> tl(sizeTabuList,0);
|
||||||
|
moTS<shiftNeighbor> localSearch2(orderShiftNH, fullEval, shiftEval, 3, tl);
|
||||||
|
// 3 is the time limit
|
||||||
|
```
|
||||||
|
In this one, the tabuList has been specified.
|
||||||
|
```c++
|
||||||
|
moTS<shiftNeighbor> localSearch3(orderShiftNH, fullEval, shiftEval,
|
||||||
|
comparator, solComparator, continuator, tl, inten, div, asp);
|
||||||
|
```
|
||||||
|
In this one, comparators, continuator, tabu list, intensification strategy, diversification strategy and aspiration criteria have been specified.
|
||||||
|
|
||||||
|
You can test these three algorithms by changing problem sizes, time limit and the size of tabu list (use parameters file or the option --vecSize=X, --timeLimit=Y and --sizeTabuList=Z on command line to execute "testSimpleTS"). It prints the initial and final solutions.
|
||||||
|
|
||||||
|
## 2. Exercise
|
||||||
|
|
||||||
|
1. Try to implement and use a diversification strategy in 'testSimpleTS". You can also use a predifined strategy: moMonOpDiversification (in "memory" directory)
|
||||||
64
mo/tutorial/Lesson5/README.md
Normal file
64
mo/tutorial/Lesson5/README.md
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
# How to use Iterated Local Search
|
||||||
|
In this lesson, an Iterated Local Search is presented. The Tabu Search of the Lesson 4 is used with an order neighborhood based on a shift operator, to solve the Queen problem.
|
||||||
|
|
||||||
|
1. Iterated Tabu Search on the Queen problem.
|
||||||
|
2. Exercise
|
||||||
|
|
||||||
|
## 1. Iterated Tabu Search (example on the Queen problem)
|
||||||
|
|
||||||
|
As in Lesson 4, you have to define a Solution, the method to initialize and evaluate it. Then you have to define a Tabu Search.
|
||||||
|
|
||||||
|
Declaration of the Tabu Search:
|
||||||
|
```c++
|
||||||
|
moTS<shiftNeighbor> ts(orderShiftNH, fullEval, shiftEval, 1, 7);
|
||||||
|
```
|
||||||
|
|
||||||
|
To use a simple Iterated Local Search, a mutation operator is needed. So the swap mutation defined in EO is used:
|
||||||
|
```c++
|
||||||
|
eoSwapMutation<Queen> mut;
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, a simple Iterated Tabu Search can be declared as follow:
|
||||||
|
```c++
|
||||||
|
moILS<shiftNeighbor> localSearch1(ts, fullEval, mut, 3);
|
||||||
|
```
|
||||||
|
This constructor has got 4 parameters:
|
||||||
|
1. a local search (ts)
|
||||||
|
2. a full evaluation function (fullEval)
|
||||||
|
3. a mutation operator (mut)
|
||||||
|
4. a number of iterations (3)
|
||||||
|
|
||||||
|
**localSearch1** performs the Tabu Search 3 times. The first solution of each iteration(except the first one) is obtained by applying the mutation operator on the last visited solution.
|
||||||
|
|
||||||
|
A constructor allows to specify the continuator. **_Be carefull_**, the continuator must be templatized by a "moDummyNeighbor":
|
||||||
|
```c++
|
||||||
|
moIterContinuator<moDummyNeighbor<Queen> > cont(4, false);
|
||||||
|
```
|
||||||
|
The explorer of the Iterated local search don't use its own neighborhood. Here, the neighborhood of the Tabu Search is used. But to respect the conception, we create a "moDummyNeighbor" using as template for Iterated Local Search.
|
||||||
|
|
||||||
|
An Iterated Tabu Search with this continuator can be declared as:
|
||||||
|
```c++
|
||||||
|
moILS<shiftNeighbor> localSearch2(ts, fullEval, mut, cont);
|
||||||
|
```
|
||||||
|
|
||||||
|
A general constructor is available allowing to specify the perturbation operator and the acceptance criteria. First, you have to declare a perturbation operator:
|
||||||
|
```c++
|
||||||
|
moMonOpPerturb<shiftNeighbor> perturb(mut, fullEval);
|
||||||
|
```
|
||||||
|
And, the acceptance criteria:
|
||||||
|
```c++
|
||||||
|
moSolComparator<Queen> solComp;
|
||||||
|
moBetterAcceptCrit<shiftNeighbor> accept(solComp);
|
||||||
|
```
|
||||||
|
Finally, the Iterated Local Search can be declared as:
|
||||||
|
```c++
|
||||||
|
moILS<shiftNeighbor> localSearch3(ts, fullEval, cont, perturb, accept);
|
||||||
|
```
|
||||||
|
|
||||||
|
You can test these three algorithms by changing problem sizes(use parameter file or the option --vecSize=X on command line to execute "testILS"). It prints the initial and the final solutions.
|
||||||
|
|
||||||
|
## 2. Exercise
|
||||||
|
|
||||||
|
* Try to implement an Iterated Hill Climbing on the Queen problem with these caracteristics:
|
||||||
|
1. Hill Climbing with a "moShiftNeighborhood" and a "moTrueContinuator"
|
||||||
|
2. Iterated Local Search using a "moIterContinuator" and a "moNeighborhoodPerturb" with a "moSwapNeighborhood".
|
||||||
250
mo/tutorial/Lesson6/README.md
Normal file
250
mo/tutorial/Lesson6/README.md
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
# How to perform a fitness analysis?
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
A lot of tools to perform the fitness landscapes analysis are defined in paradisEO-MO:
|
||||||
|
* Density Of States
|
||||||
|
* Fitness Distance Correlation
|
||||||
|
* Autocorrelation length and autocorrelation functions
|
||||||
|
* Sampling the local optima by adaptive walks
|
||||||
|
* Neutral degree distribution
|
||||||
|
* Evolvability of neutral networks by neutral walks
|
||||||
|
* Fitness Cloud
|
||||||
|
|
||||||
|
With the same code (and effort ;-) ), you can make an apriori study of your problem with fitness landscapes analysis and use efficient solution-based metaheuristics. You can also make an aposteriori fitness landscapes analysis of your problem to explain why your metaheuristics works or not.
|
||||||
|
|
||||||
|
This lesson will let you:
|
||||||
|
* Use the fitness analysis tools of MO library
|
||||||
|
* Learn to perform your own fitness landscapes analysis
|
||||||
|
|
||||||
|
In lesson 1, you have learnt to define the fitness function. In lesson 2, you have learn to define a neighbor and a neighborhoods. This lesson will used those previous lessons.
|
||||||
|
|
||||||
|
This tutorial is made to learn how to perform a fitness landscapes analysis with paradisEO-MO. It is not a course to learn fitness landscapes analysis. You can find some information about fitness landscapes analysis here: [tutorial GECCO 09 (pdf)](http://www.i3s.unice.fr/~verel/talks/tutorialFitnessLandscapes_gecco2009.pdf) or [tutorial WCCI-CEC 10 (pdf)](http://www.i3s.unice.fr/~verel/talks/tutorialCEC2010.pdf)
|
||||||
|
|
||||||
|
## 1. I want to compute the Fitness Distance Correlation (FDC)!
|
||||||
|
|
||||||
|
You can compute the FDC. From the "paradiseo-mo/build/tutorial/Lesson6" directory, type:
|
||||||
|
```shell
|
||||||
|
./fdc -V=20 -n=500
|
||||||
|
```
|
||||||
|
|
||||||
|
Great! You have sample fitness and distance to global optimum of 500 random solutions on the oneMax problem (which maximizes the number of ones in the bit string) for the bit strings of size 20. On your output screen, you can see the fitness and distance of the first and last solution of the sample. For example:
|
||||||
|
```text
|
||||||
|
First values:
|
||||||
|
Fitness 8
|
||||||
|
Distance 12
|
||||||
|
Last values:
|
||||||
|
Fitness 6
|
||||||
|
Distance 14
|
||||||
|
```
|
||||||
|
|
||||||
|
In the file "out.dat", you have all the sample. First column is the fitness and the second column is the distance to global optimum of the 500 solutions.
|
||||||
|
|
||||||
|
After you can compute the correlation coefficient with your best statistical software such as R (with this small script) or excel with this help (import data and correlation) or with this small awk script.
|
||||||
|
|
||||||
|
I found -1 with my sample which means that it is very easy function, isn't it?
|
||||||
|
|
||||||
|
## 2. The main principles of fitness landscapes analysis with MO
|
||||||
|
|
||||||
|
The fitness landscapes analysis is based on a sampling of the search space. During this sampling, data are collected, and then some statistics can be computed to deduce the structure of the search space.
|
||||||
|
|
||||||
|
The class to define a sampling is moSampling<Neighbor> in the directory mo/src/sampling. All classes of the standard tools of fitness landscapes analysis inherit from this class (see documentation):
|
||||||
|
* moDensityOfStatesSampling : density of states (distribution of fitness values)
|
||||||
|
* moAutocorrelationSampling : autocorrelation length and functions
|
||||||
|
* moFDCsampling : fitness distance correlation
|
||||||
|
* moHillClimberSampling : adaptive walks
|
||||||
|
* moNeutralDegreeSampling : neutral degree
|
||||||
|
* moNeutralWalkSampling : evolvability of neutral networks
|
||||||
|
* moFitnessCloudSampling : evolvability of the operator, and the neighborhood
|
||||||
|
|
||||||
|
The constructor of moSampling is:
|
||||||
|
```c++
|
||||||
|
moSampling (eoInit< EOT > &_init, moLocalSearch< Neighbor > &_localSearch, moStat< EOT, ValueType > &_stat, bool _monitoring=true)
|
||||||
|
```
|
||||||
|
|
||||||
|
As usual in paradiseo, EOT is the typedef of the solution, and Neighbor is the typedef of the neighbor (see lesson 1). This constructor needs an initialization methods (see tutorial on paradiseo-eo), a local search which perform the sampling of the search space (see previous lessons to define it), and a object which able to compute a statistic. At each iteration of the local search, the given statistic is computed, and is saved if boolean monitoring is true.
|
||||||
|
|
||||||
|
The statistics inherit from the class moStat. The include file can be found in mo/src/continuator directory. The pre-defined statistics are:
|
||||||
|
* moFitnessStat : the fitness of the current solution
|
||||||
|
* moDistanceStat : the distance between the current solution and a given solution
|
||||||
|
* moSolutionStat : the current solution
|
||||||
|
* moCounterStat : the number of iterations
|
||||||
|
* moBestSoFarStat : the best current solution found
|
||||||
|
* moNeighborBestStat : best fitness over k neighbors
|
||||||
|
* moNeighborhoodStat : to compute the statistics from the neighbors solutions :
|
||||||
|
* moAverageFitnessNeighborStat : average fitness in the neighborhood
|
||||||
|
* moStdFitnessNeighborStat : standard deviation of fitness
|
||||||
|
* moMaxNeighborStat : maximum fitness
|
||||||
|
* moMinNeighborStat : minimum fitness
|
||||||
|
* moSecondMomentNeighborStat : average and standard deviation
|
||||||
|
* moSizeNeighborStat : size of the neighborhood
|
||||||
|
* moNeutralDegreeNeighborStat : number of neighbors with equal fitness
|
||||||
|
* moNbInfNeighborStat : number of neighbors with lower fitness
|
||||||
|
* moNbSupNeighborStat : number of neighbor with higher fitness
|
||||||
|
|
||||||
|
All those statistics can be used in the sampling. Of course you can define your own statistic class. Several statistics can be collected at each iteration: use the method add of the class moSampling to collect another statistic.
|
||||||
|
|
||||||
|
For standard tools of fitness landscapes analysis, there is no need to give the sampling method, and the statistics objects. You only have to give which is specific to your problem such as the initialization method, the fitness function, the neighborhood, or the evaluation function of a neighbor.
|
||||||
|
|
||||||
|
## 3. Browsing the code
|
||||||
|
|
||||||
|
Please, open the file "mo/tutorial/Lesson6/fdc.cpp", and follow me in the code:
|
||||||
|
|
||||||
|
### 1. The includes part:
|
||||||
|
|
||||||
|
The general includes for the c++ stdlib streams:
|
||||||
|
```c++
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
This includes for eo which contains all include files of EO:
|
||||||
|
```c++
|
||||||
|
#include <eo>
|
||||||
|
```
|
||||||
|
|
||||||
|
The first line to include the bit string representation defined in eo, and the second one to include the bit string neighbor representation. All classical problem-dependent part of MO are defined in the sub-directory "problems". How to define your representation is explained in EO tutorial, and how to design your neighbor is explained in the lesson 2. Here just use it.
|
||||||
|
```c++
|
||||||
|
#include <ga/eoBit.h>
|
||||||
|
#include <problems/bitString/moBitNeighbor.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
This includes the evaluation function of a solution (full evaluation). There is no evaluation function for the neighbor because there is no need in FDC. Some others tools such as autocorrelation neighbor evaluation is defined such as the lesson 1.
|
||||||
|
```c++
|
||||||
|
#include <problems/eval/oneMaxFullEval.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
The fitness distance correlation is the correlation between the fitness of solution and the distance to global optimum (or at least to some best known solution). So, this include file uses the Hamming distance.
|
||||||
|
```c++
|
||||||
|
#include <utils/eoDistance.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can include the FDC tool.
|
||||||
|
```c++
|
||||||
|
#include <sampling/moFDCsampling.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. The typedef part:
|
||||||
|
|
||||||
|
EO can apply an evolutionary algorithm on any type of solution. So, all EO classes are parametrized by the type of solutions, and it is useful to use a synonym (with a typedef) of the solution's type.
|
||||||
|
MO can apply an local search algorithm on any type of solution and neighbor. So, for the same reason, all classes of MO are parametrized by the neighbor's type. In the neighbor class, the solution's type is defined. More precision on the neighbor design will be given in the lesson 2.
|
||||||
|
Here the solution representation is a bit string and the neighbor representation is related to a bit string solution and Hamming distance 1 (only 1 bit can be flipped), both using an "unsigned int" fitness value.
|
||||||
|
```c++
|
||||||
|
typedef eoBit<unsigned int> Indi;
|
||||||
|
typedef moBitNeighbor<unsigned int> Neighbor;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Object definition part:
|
||||||
|
|
||||||
|
Follows the main function "main_function" where all useful objects are defined.\\
|
||||||
|
First, a code to parse the command line and a file. It gives the value of the random seed and the size of bit string. The lesson 3 of EO tutorial gives more precision on this code. Here we have only to understand that the variables "seed" and "vecSize" are initialized.
|
||||||
|
```c++
|
||||||
|
eoParser parser(argc, argv);
|
||||||
|
|
||||||
|
eoValueParam<uint32_t> seedParam(time(0), "seed", "Random number seed", 'S');
|
||||||
|
parser.processParam( seedParam );
|
||||||
|
unsigned seed = seedParam.value();
|
||||||
|
|
||||||
|
// length of the bit string
|
||||||
|
eoValueParam<unsigned int> vecSizeParam(20, "vecSize", "Genotype size", 'V');
|
||||||
|
parser.processParam( vecSizeParam, "Representation" );
|
||||||
|
unsigned vecSize = vecSizeParam.value();
|
||||||
|
|
||||||
|
// the number of solution sampled
|
||||||
|
eoValueParam<unsigned int> solParam(100, "nbSol", "Number of random solution", 'n');
|
||||||
|
parser.processParam( solParam, "Representation" );
|
||||||
|
unsigned nbSol = solParam.value();
|
||||||
|
|
||||||
|
// the name of the output file
|
||||||
|
string str_out = "out.dat"; // default value
|
||||||
|
eoValueParam<string> outParam(str_out.c_str(), "out", "Output file of the sampling", 'o');
|
||||||
|
```
|
||||||
|
|
||||||
|
To seed the random seed (see lesson 1 of EO tutorial for more precision):
|
||||||
|
```c++
|
||||||
|
rng.reseed(seed);
|
||||||
|
```
|
||||||
|
|
||||||
|
The definition the initialization of solutions is not defined is MO but in EO. The "eoInitFixedLength" is a class that makes a random intialization of bit string of a given length. Each bit is true with 1/2 rate. You can see the lesson 1 of EO tutorial lesson 1 for more precision.
|
||||||
|
```c++
|
||||||
|
eoUniformGenerator<bool> uGen;
|
||||||
|
eoInitFixedLength<Indi> random(vecSize, uGen);
|
||||||
|
```
|
||||||
|
|
||||||
|
The fitness function of the oneMax problem is the number of 1 in the bit string. It is already defined in MO:
|
||||||
|
```c++
|
||||||
|
oneMaxFullEval<Indi> fullEval;
|
||||||
|
```
|
||||||
|
|
||||||
|
The distance used is the classical Hamming distance:
|
||||||
|
```c++
|
||||||
|
eoHammingDistance<Indi> distance;
|
||||||
|
```
|
||||||
|
|
||||||
|
For this analysis, the best solution is needed: the solution with all 1s.
|
||||||
|
```c++
|
||||||
|
Indi bestSolution(vecSize, true); // global optimum
|
||||||
|
```
|
||||||
|
|
||||||
|
All representation-dependent part is now defined, so the FDC sampling can be defined. The constructor needs the initialization, the fitness function, the distance used, the reference solution, and the size of the sample:
|
||||||
|
```c++
|
||||||
|
moFDCsampling<Neighbor> sampling(random, fullEval, distance, bestSolution, nbSol);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. The execution of sampling part:
|
||||||
|
|
||||||
|
Now apply your sampling as follows:
|
||||||
|
```c++
|
||||||
|
sampling();
|
||||||
|
```
|
||||||
|
|
||||||
|
This sampling uses the initialization method to define a pure random search, and at each iteration the fitness and the distance are computed.
|
||||||
|
|
||||||
|
4. The export part:
|
||||||
|
|
||||||
|
To export your sample into a file:
|
||||||
|
```c++
|
||||||
|
sampling.fileExport(str_out);
|
||||||
|
```
|
||||||
|
|
||||||
|
The first column of the file is the fitness and the second the distance from the global optimum.
|
||||||
|
|
||||||
|
Maybe you may want to read the data from your c++ code. So it is possible to export the data into a vector:
|
||||||
|
```c++
|
||||||
|
const std::vector<double> & fitnessValues = sampling.getValues(0);
|
||||||
|
const std::vector<double> & distValues = sampling.getValues(1);
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the indexes of the statistics (here 0 and 1) are in the same order of the declaration with the constructor and the "add" method of moSampling.
|
||||||
|
|
||||||
|
After you can use the vector as you want:
|
||||||
|
```c++
|
||||||
|
std::cout << "Fitness " << fitnessValues[0] << std::endl;
|
||||||
|
std::cout << "First values:" << std::endl;
|
||||||
|
std::cout << "Distance " << distValues[0] << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Last values:" << std::endl;
|
||||||
|
std::cout << "Fitness " << fitnessValues[fitnessValues.size() - 1] << std::endl;
|
||||||
|
std::cout << "Distance " << distValues[distValues.size() - 1] << std::endl;
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Easy, isn't it?
|
||||||
|
|
||||||
|
## 4. Others fitness landscapes tools
|
||||||
|
|
||||||
|
The other tools can be used in the same way. For each tool, an example has been made. Please read the code of:
|
||||||
|
|
||||||
|
* densityOfStates.cpp : density of states example
|
||||||
|
* autocorrelation.cpp : autocorrelation length and functions
|
||||||
|
* adaptiveWalks.cpp : sampling by hill-climbings, length of adaptative walks
|
||||||
|
* fdc.cpp : ;-)
|
||||||
|
* fitnessCloud.cpp : bivariate density of fitness of solutions and fitness of neighbors
|
||||||
|
* neutralDegree.cpp : number of neighbor with the same fitness
|
||||||
|
* neutralWalk.cpp : evolvability of the neutral networks
|
||||||
|
* sampling.cpp : general sampling method
|
||||||
|
|
||||||
|
If you have some questions or remarks, please contact us! sebastien.verel aaattt unice.fr or member of the development team.
|
||||||
BIN
mo/tutorial/Lesson6/multimodalFitnessLandscape.jpg
Executable file
BIN
mo/tutorial/Lesson6/multimodalFitnessLandscape.jpg
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
60
mo/tutorial/Lesson7/README.md
Normal file
60
mo/tutorial/Lesson7/README.md
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# How to hybrid an evolutionary algorithm and a local search
|
||||||
|
In this lesson, a hybridization between an evolutionary algorithm(EA) and a local search is presented. It will be illustrated by an example on the Queen problem. Here, the hybridization consists in replacing the mutation operator of the EA by a first improvement hill climber.
|
||||||
|
|
||||||
|
1. hybridization
|
||||||
|
2. Exercise
|
||||||
|
|
||||||
|
## 1. Hybridization (example on the Queen problem)
|
||||||
|
|
||||||
|
First, you have to define the represenation of a Queen, how to initialize and how to evaluate a population of solutions:
|
||||||
|
```c++
|
||||||
|
queenFullEval<Queen> fullEval;
|
||||||
|
|
||||||
|
eoInitPermutation<Queen> init(vecSize);
|
||||||
|
|
||||||
|
eoPop<Queen> pop;
|
||||||
|
Queen tmp;
|
||||||
|
for(unsigned int i=0; i<20; i++){ //population size is fixed to 20
|
||||||
|
init(tmp);
|
||||||
|
fullEval(tmp);
|
||||||
|
pop.push_back(tmp);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As in previous lessons, a local search is declared (first improvement hill climber):
|
||||||
|
```c++
|
||||||
|
moFullEvalByCopy<shiftNeighbor> shiftEval(fullEval);
|
||||||
|
|
||||||
|
orderShiftNeighborhood orderShiftNH(pow(vecSize-1, 2));
|
||||||
|
|
||||||
|
moFirstImprHC<shiftNeighbor> hc(orderShiftNH, fullEval, shiftEval);
|
||||||
|
```
|
||||||
|
To hybrid this local search with an EA, you just have to use it instead of a classical mutation:
|
||||||
|
```c++
|
||||||
|
eoOrderXover<Queen> cross;
|
||||||
|
eoSGATransform<Queen> transform(cross, 0.3, hc, 0.7); // cross and mutation probabilities are fixed
|
||||||
|
```
|
||||||
|
|
||||||
|
Others components of the "eoEasyEA" have to be declared:
|
||||||
|
```c++
|
||||||
|
eoGenContinue<Queen> EAcont(50); //nb generations is fixed to 50
|
||||||
|
eoDetTournamentSelect<Queen> selectOne(2); //size of tournament is fixed to 2
|
||||||
|
eoSelectMany<Queen> select(selectOne, 1); //rate of selection is fixed to 1
|
||||||
|
eoGenerationalReplacement<Queen> repl;
|
||||||
|
```
|
||||||
|
More details are available in EO lessons.
|
||||||
|
|
||||||
|
Finally, the hybrid algorithm is declared as:
|
||||||
|
```c++
|
||||||
|
eoEasyEA<Queen> hybridAlgo(EAcont, fullEval, select, transform, repl);
|
||||||
|
```
|
||||||
|
and should be applied on the population with:
|
||||||
|
```c++
|
||||||
|
hybridAlgo(pop);
|
||||||
|
```
|
||||||
|
|
||||||
|
You can test this hybrid algorithm by changing problem size (use parameters file or the option --vecSize=X on command line to execute "hybridAlgo"). It prints the initial and final population.
|
||||||
|
|
||||||
|
## 2. Exercise
|
||||||
|
|
||||||
|
Try to use a hybridization at the checkpointing step rather than at the mutation step. You have to implement an "eoUpdater" which applies a local search. This updater should be added in a "eoCheckpoint".
|
||||||
Loading…
Add table
Add a link
Reference in a new issue