Variation
Operators
Variation operators modify individuals, or, equivalently, move them
in the search space. They are almost always stochastic,
i.e. they are based on random numbers, or equivalently, perform random
modifications of their arguments. Variation operators are classified depending
on the number of arguments they use and/or modify.
Variation operators involving two individuals are called crossover
operators. They can either modify one of the parents according to the
material of the other parent, or modify both parents. In EO, the former
are called Binary operators and the latter Quadratic operators.
Variation operators involving one single individual are called mutation
operators.
In EO you can also define and use variation operators that generate
any number of offspring from any number of parents (sometimes termed orgy
operators). They are called general operators.
Though most the historical evolutionary algorithms used at most one crossover and one mutation (see e.g. the Simple Genetic Algorithm in lesson1), the trend now in evolutionary computation is to combine operators, choosing at run-time and for each individual which operator to apply. This can be done in the framework of simple operators, combining for instance several mutations into a variation operator that chooses one of them according to user-defined preferences: such combinations are called in EO proportional combination of simple operators (see e.g. how to define and use such combined operators in the SGA of lesson2).
Finally, there are many other ways to combine different variation operators of different kind. Within EO, you can choose to apply many different types of operator to the population, either in turn (this is called sequential combination) or by randomly choosing among a set of operators at a given time (this is proportional combination, generalizing the one defined for simple operators). You can of course mix and interleave both approaches at will, and this is described as general combination of general operators.
EO implementation: all variation
operators in EO derive from the base (abstract) class eoOp
(as usual, click to see the inheritance diagram). blabla
The characteristic of crossover operators is that they involve two parents. However, there are crossover operators that generate two parents, and some that generate one parent only, and both types are available in EO. The former type (2 --> 2) is termed quadratic crossover operator, and is implemanted in the eoQuadOp class; the latter type (2 --> 1) is termed binary operator and is implemanted in class eoBinOp. Both classes are, as usual, templatized by the type of individual they can handle (see documentation for eoBinOp and eoQuadOp).
Note: Whereas it is straightforward to create a binary crossover operator from a quadratic one (by discarding the changes on the second parent), the reverse might prove impossible (imagine a binary crossover that simply merges the parents material: there is no way to generate two new parents from that!).
Interfaces:
The general approach in EO about simple variation operators is to perform
in-place
modifications, i.e. modifying the arguments rather than generating
new (modified) individuals. This results in the following interfaces for
the functor objects eoBinOp and eoQuadOp:
void operator()(EOT & , const EOT &)
for eoBinOp (note the const)
void operator()(EOT & , EOT &
)
for eoQuadOp
which you could have guessed from the inheritance diagrams up to the eoBF abstract class. You can also guess that only the first argument will be modified by an oeBin object, while both arguments will be modified by an eoQuad object.
Using crossover operators:
Directly applying crossover operators is straightforward from the interface
above:
eoBinOpDerivedClass<Indi> myBinOp(parameters);
// use constructor to pass
eoQuadOpDerivedClass<Indi> myQuadOp(parameters);
// any useful argument
Indi eo1= ..., eo2= ...; //
the candidates to crossover
myBinOp(eo1, eo2);
// will modify eo1 only
myQuadOp(eo1, eo2);
// will modify eo1 and eo2
However, you will hardly have to do so, as operators are used within
other classes, and are applied systematically to whole sets of individuals
(e.g. that have already been selected, in standard generational evolutionary
algorithms).
Hence the way to use such operators will more likely ressemble, if
you are using for instance an SGA, this (definition,
usage).
See also the different ways that are described below, encapsulating the
operators into combined operators objects.
Writing a crossover
operator:
There are only two things to modify in the template
class definitions provided (apart from the name of the class you are
creating!)
Interfaces:
The general approach in EO about simple variation operators is to perform
in-place
modifications, i.e. modifying the arguments rather than generating
new (modified) individuals. This results in the following interface for
the functor objects eoMonOp:
void operator()(EOT & )
which you could have guessed from the inheritance diagrams up to the eoUF abstract class.
Using mutation operators:
Directly applying mutation operators is straightforward from the interface
above:
eoMonOpDerivedClass<Indi> myMutation(parameters);
//pass parameters in constructor
Indi eo1 = ...;
// eo1 is candidate to mutation
myMutation(eo1);
// will modify eo1
However, you will hardly have to do so, as operators are used within
other classes, and are applied systematically to whole sets of individuals
(e.g. that have already been selected, in standard generational evolutionary
algorithms).
Hence the way to use such operators will more likely ressemble, if
you are using for instance an SGA, this (definition,
usage).
See also the different ways that are described below, encapsulating the
operators into combined operators objects.
Writing a mutation
operator:
There are only two things to modify in the template
class definitions provided (apart from the name of the class you are
creating!)
The best thing to do is to go to the Lesson2 of the tutorial, where everything is explained.
There are two main ways to use and combine general operators in EO: the proportional combination, similar to what has been described for simple operators above, and the sequential combination, which amounts to apply all operators in turn to a bunch of individuals, each operator being applied with a specific probability.
Proportional combinations behave like a unique operator: when it is called upon a population of candidates, an eoProportionalOpContainer enters the following loop:
while there are individuals left in the candidate population
for all operators it contains, apply the operator to the candidate population, that is
Remark: there is actually a single list
of individuals that is maintained through a clever mecahnism of mark, rewind
and unmark, but that's a purely technical matter. If you are interested,
go and check the eoOpContainer and the eoSequentialOpContainer code.
Adding operators to a container:
The basic function to add an operator to an eoOpContainer is the method
add
from class eoOpContainer.
It is similar to all other add
methods in other Combined things in eo (as the simple eoProportionalCombinedXXXop
described above, but also the eoCombinedContinue class or the eoCheckPoint
class).
The syntax is straightforward, and it works with any of the operator
classes defined above:
someOperatorType<Indi> myOperator;
eoXXXOpContainer<Indi> myOpContainer;
myOpContainer.add(myOperator, rate); //
rate: double whose meaning depends on XXX
where XXX can be one of Proportional and Sequential.
However, the way rate
will be used is highly dependent on the type of OpContainer your are creating
there: