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

@ -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);
```