Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mo tutorial doc in Markdown format #65

Merged
merged 1 commit into from
May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
448 changes: 448 additions & 0 deletions mo/tutorial/Lesson1/README.md

Large diffs are not rendered by default.

Binary file added mo/tutorial/Lesson1/schemaLS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
124 changes: 124 additions & 0 deletions mo/tutorial/Lesson2/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 110 additions & 0 deletions mo/tutorial/Lesson3/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 67 additions & 0 deletions mo/tutorial/Lesson4/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 64 additions & 0 deletions mo/tutorial/Lesson5/README.md
Original file line number Diff line number Diff line change
@@ -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".
Loading