Add tutorial READMEs and fix tutorial return codes

Add README.md files for moeo/tutorial/Lesson{1-4}, smp/tutorial/Lesson{1-4},
and mo/tutorial/Lesson9 — these tutorials had no documentation.

Fix return 1 → return 0 in 28 tutorial main() functions across mo/ and
smp/ that unconditionally returned failure status.
This commit is contained in:
Eremey Valetov 2026-02-28 19:24:05 -05:00
commit c1a44fd2a6
37 changed files with 371 additions and 30 deletions

View file

@ -218,5 +218,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -247,5 +247,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -214,5 +214,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -192,5 +192,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -209,5 +209,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -214,5 +214,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -211,5 +211,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -196,5 +196,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -192,5 +192,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -193,5 +193,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -237,5 +237,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -228,5 +228,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -262,5 +262,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -226,5 +226,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -207,5 +207,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -222,5 +222,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -190,5 +190,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -188,5 +188,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -213,5 +213,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -211,5 +211,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -247,5 +247,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -252,5 +252,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -197,5 +197,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -274,5 +274,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -239,5 +239,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -218,5 +218,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -0,0 +1,48 @@
# Variable Neighborhood Search (VNS)
VNS on the N-Queens problem, using multiple neighborhood structures to
escape local optima.
The N-Queens problem places N queens on an N x N chessboard so that no two
queens attack each other. The representation is a permutation (one queen
per column) and the fitness counts conflicts (minimized).
## Running
From the `build/mo/tutorial/Lesson9` directory:
```shell
./VNS -V=12
```
This runs VNS on a 12-queens instance with a 3-second time limit.
## How it works
VNS alternates between perturbation (shaking) and local search using
different neighborhood structures. Here, two neighborhoods are used:
```c++
shiftNeighborhood shiftNH((vecSize-1) * (vecSize-1));
swapNeighborhood swapNH(vecSize * (vecSize-1) / 2);
```
Each has its own hill-climber and mutation operator:
```c++
moSimpleHC<shiftNeighbor> ls1(shiftNH, fullEval, shiftEval);
moSimpleHC<swapNeighbor> ls2(swapNH, fullEval, swapEval);
moRndVectorVNSelection<Queen> selectNH(ls1, shiftMut, true);
selectNH.add(ls2, swapMut);
```
`moRndVectorVNSelection` randomly picks which neighborhood/local search
to apply at each iteration. Forward (`moForwardVectorVNSelection`) and
backward (`moBackwardVectorVNSelection`) selection strategies are also
available. The search runs until the `moTimeContinuator` time limit:
```c++
moTimeContinuator<shiftNeighbor> cont(3); // 3 seconds
moVNS<shiftNeighbor> vns(selectNH, acceptCrit, fullEval, cont);
```

View file

@ -284,5 +284,5 @@ int main(int argc, char **argv)
catch (exception& e) {
cout << "Exception: " << e.what() << '\n';
}
return 1;
return 0;
}

View file

@ -0,0 +1,84 @@
# How to run your first multi-objective EA?
In this lesson you will solve the Schaffer SCH1 bi-objective problem with
NSGA-II using the ParadisEO-MOEO framework.
The SCH1 problem minimizes two objectives over a single real-valued
variable x in [0, 2]:
- f1(x) = x^2
- f2(x) = (x - 2)^2
## 1. Running
From the `build/moeo/tutorial/Lesson1` directory:
```shell
./Sch1 --help
./Sch1 --popSize=100 --maxGen=100
```
Parameters can also be read from a file:
```shell
./Sch1 @Sch1.param
```
## 2. Browsing the code
Open `Sch1.cpp` and follow along.
First, define the objective vector traits. This tells MOEO how many
objectives the problem has and whether each is minimizing or maximizing:
```c++
class Sch1ObjectiveVectorTraits : public moeoObjectiveVectorTraits
{
public:
static bool minimizing (int) { return true; }
static bool maximizing (int) { return false; }
static unsigned int nObjectives () { return 2; }
};
```
The solution class `Sch1` inherits from `moeoRealVector` — a real-valued
decision vector that also carries an objective vector:
```c++
class Sch1 : public moeoRealVector < Sch1ObjectiveVector >
{
public:
Sch1() : moeoRealVector < Sch1ObjectiveVector > (1) {}
};
```
The evaluator computes both objectives and stores them via
`objectiveVector()`:
```c++
void operator () (Sch1 & _sch1)
{
if (_sch1.invalidObjectiveVector()) {
Sch1ObjectiveVector objVec;
double x = _sch1[0];
objVec[0] = x * x;
objVec[1] = (x - 2.0) * (x - 2.0);
_sch1.objectiveVector(objVec);
}
}
```
The algorithm itself is a single line — `moeoNSGAII` takes the generation
limit, evaluator, crossover, and mutation as constructor arguments:
```c++
moeoNSGAII < Sch1 > nsgaII (MAX_GEN, eval, xover, P_CROSS, mutation, P_MUT);
```
After the run, extract the Pareto front with `moeoUnboundedArchive`:
```c++
moeoUnboundedArchive < Sch1 > arch;
arch(pop);
arch.sortedPrintOn(cout);
```

View file

@ -0,0 +1,31 @@
# Multi-objective EA on the flow-shop scheduling problem
This lesson solves a more realistic combinatorial problem: the bi-objective
permutation flow-shop, which minimizes both makespan and total flow time.
## Running
From the `build/moeo/tutorial/Lesson2` directory:
```shell
./FlowShopEA @FlowShopEA.param
```
The parameter file specifies the problem instance, population size, operator
rates, and fitness assignment strategy.
## How it works
The code uses ParadisEO's `do_make_*` factory functions to build all
components from command-line parameters:
```c++
eoEvalFuncCounter<FlowShop>& eval = do_make_eval(parser, state);
eoInit<FlowShop>& init = do_make_genotype(parser, state);
eoGenOp<FlowShop>& op = do_make_op(parser, state);
```
The flow-shop representation is defined in `FlowShop.h` — a
permutation-based `moeoVector` with a bi-objective vector. Different
Pareto-based fitness assignment strategies (NSGA-II, SPEA2, IBEA, etc.)
can be selected via parameters.

View file

@ -0,0 +1,32 @@
# User-friendly multi-objective EA
Same flow-shop problem as Lesson 2, but with a complete algorithmic pipeline
built from reusable `do_make_*` helpers: stopping criteria, checkpointing,
statistics, and the full evolution engine.
## Running
From the `build/moeo/tutorial/Lesson3` directory:
```shell
./FlowShopEA2 @FlowShopEA2.param
```
## How it works
The main program is essentially a sequence of factory calls:
```c++
eoEvalFuncCounter<FlowShop>& eval = do_make_eval(parser, state);
eoInit<FlowShop>& init = do_make_genotype(parser, state);
eoPop<FlowShop>& pop = do_make_pop(parser, state, init);
eoContinue<FlowShop>& term = do_make_continue_moeo(parser, state, eval);
eoCheckPoint<FlowShop>& checkpoint = do_make_checkpoint_moeo(parser, state, ...);
eoAlgo<FlowShop>& algo = do_make_ea_moeo(parser, state, ...);
do_make_run(parser, state, algo, pop);
```
`do_make_continue_moeo` builds continuators (generation limit, fitness
target, etc.) from parameters. `do_make_checkpoint_moeo` adds statistics,
population snapshots, and archive tracking. `do_make_ea_moeo` constructs
the selection and replacement strategy.

View file

@ -0,0 +1,35 @@
# Dominance-based multi-objective local search (DMLS)
Same flow-shop problem as Lessons 2-3, but solved with local search instead
of an evolutionary algorithm. This combines ParadisEO-MO local search
components with the MOEO multi-objective framework.
## Running
From the `build/moeo/tutorial/Lesson4` directory:
```shell
./FlowShopDMLS @FlowShopDMLS.param
```
## How it works
The program uses `moShiftNeighbor` and `moOrderNeighborhood` from
ParadisEO-MO, with `moeoFullEvalByCopy` to adapt the full solution
evaluator for neighbor evaluation:
```c++
typedef moShiftNeighbor<FlowShop, FlowShopObjectiveVector> Neighbor;
moOrderNeighborhood<Neighbor> neighborhood((nhSize-1) * (nhSize-1));
moeoFullEvalByCopy<Neighbor> moEval(eval);
```
At each iteration, DMLS explores all non-dominated solutions in the
archive via local search moves and updates the archive with any improving
neighbors:
```c++
moeoFirstImprovingNeighborhoodExplorer<Neighbor> explor(neighborhood, moEval);
moeoUnifiedDominanceBasedLS<Neighbor> algo(checkpoint, eval, arch, explor, select);
```

View file

@ -0,0 +1,23 @@
# Master/Workers wrapping with eoEasyEA
Wraps a standard eoEasyEA with the ParadisEO-SMP parallel evaluation model,
applied to the Quadratic Assignment Problem (QAP).
The QAP assigns facilities to locations to minimize cost (flow x distance).
The representation is a permutation.
## Running
From the `build/smp/tutorial/Lesson1` directory:
```shell
./lesson1_eoEasyEA lesson1_eoEasyEA.param
```
## How it works
The key idea: ParadisEO-SMP parallelizes evaluation by wrapping an existing
sequential algorithm. The algorithm code itself stays the same. `QAP.h`
defines the problem representation and evaluation, `QAPGA.h` defines the GA
operators, and `parserStruct.h` + `utils.h` handle parameter parsing and
instance loading. See also `lesson1_data.dat` for the problem instance.

View file

@ -23,8 +23,8 @@ same conditions as regards security.
The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.
ParadisEO WebSite : http://paradiseo.gforge.inria.fr
Contact: paradiseo-help@lists.gforge.inria.fr
ParadisEO WebSite : https://nojhan.github.io/paradiseo/
Contact: https://github.com/nojhan/paradiseo/issues
*/
/**
@ -125,5 +125,5 @@ int main(int argc, char **argv)
delete[] b;
return 1;
return 0;
}

View file

@ -0,0 +1,30 @@
# Homogeneous island model
Three islands running the same algorithm (`eoEasyEA`) with a complete
topology — all islands exchange individuals with all others.
Same QAP problem as Lesson 1.
## Running
From the `build/smp/tutorial/Lesson2` directory:
```shell
./lesson2_homogeneous
```
The problem instance is loaded from `../lessonData.dat`.
## How it works
All islands share the same operators (evaluation, selection, crossover,
mutation, replacement) but can have different parameters. The topology
and island model are set up with:
```c++
Topology<Complete> topo;
IslandModel<Indi> model(topo);
```
Each island gets its own population and generation limit, then the model
runs them in parallel and handles migration.

View file

@ -0,0 +1,27 @@
# Heterogeneous island model
Two islands running different algorithms: an `eoEasyEA` on the QAP problem
and a PSO on a bitstring problem, connected via conversion functions.
## Running
From the `build/smp/tutorial/Lesson3` directory:
```shell
./lesson3_heterogeneous
```
## How it works
When islands use different representations, you need conversion functions
to translate individuals during migration. In this example, `fromBase()`
and `toBase()` convert between the QAP permutation and the PSO bitstring:
```c++
Indi2 fromBase(Indi& i, unsigned size) { ... }
Indi toBase(Indi2& i) { ... }
```
The conversions here are dummy placeholders (they just create random
individuals), but in a real application you would implement meaningful
mappings between representations.

View file

@ -0,0 +1,31 @@
# Dynamic island topology
Three islands start with a ring topology and switch to a complete topology
after 10 seconds of computation.
Same QAP problem as Lessons 1-2.
## Running
From the `build/smp/tutorial/Lesson4` directory:
```shell
./lesson4_topology
```
## How it works
A callback function registered with the islands triggers the topology
change:
```c++
void changeTopo(IslandModel<Indi>* _model, AbstractTopology& _topo)
{
// ... after 10 seconds:
_model->setTopology(_topo);
}
```
The topology can be changed at runtime via `model.setTopology()`, so you
can implement adaptive communication patterns based on time, convergence,
or any other criterion.