Skip to content

Commit ecef336

Browse files
committed
better documentation
1 parent 3ef768b commit ecef336

19 files changed

+326
-159
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,8 @@ Here we list the [algorithms](#41-implemented-algorithms), [search spaces](#42-i
673673
1. Simple [Hill Climber](https://thomasweise.github.io/moptipy/moptipy.algorithms.so.html#moptipy.algorithms.so.hill_climber.HillClimber) creates a random solution as initial best-so-far solution and then iteratively applies the unary search operator to the best-so-far solution. When the result of the unary operator is better, it becomes the new best-so-far solution, otherwise it is discarded.
674674
2. [Hill Climber with Restarts](https://thomasweise.github.io/moptipy/moptipy.algorithms.so.html#moptipy.algorithms.so.hill_climber_with_restarts.HillClimberWithRestarts) works exactly like the hill climber, but restarts at a new random solution after a fixed number of unsuccessful moves.
675675
3. [Random Local Search / (1+1)-EA](https://thomasweise.github.io/moptipy/moptipy.algorithms.so.html#moptipy.algorithms.so.rls.RLS) (RLS) works like the [hill climber](https://thomasweise.github.io/moptipy/moptipy.algorithms.so.html#moptipy.algorithms.so.hill_climber.HillClimber) as well, but accepts a new solution if it is *not worse* than the best-so-far solution (instead of requiring it to be strictly *better*, as the hill climber does).
676-
4[(mu+lambda)-EA](https://thomasweise.github.io/moptipy/moptipy.algorithms.so.html#moptipy.algorithms.so.ea.EA) is a simple population-based metaheuristic that starts with a population of `mu` random solutions. In each iteration, it retains only the `mu` best solutions from the population ("best" in terms of the objective value, ties are broken such that newer solutions are preferred). It then applies the unary operator and the binary operator to generate `lambda` new solutions and adds them to the population. The `(1+1)-EA` with `br=0` probability to use the binary operator is equivalent to [RLS](https://thomasweise.github.io/moptipy/moptipy.algorithms.html#moptipy.algorithms.rls.RLS).
676+
4. [(mu+lambda)-EA](https://thomasweise.github.io/moptipy/moptipy.algorithms.so.html#moptipy.algorithms.so.ea.EA) is a simple population-based metaheuristic that starts with a population of `mu` random solutions. In each iteration, it retains only the `mu` best solutions from the population ("best" in terms of the objective value, ties are broken such that newer solutions are preferred). It then applies the unary operator and the binary operator to generate `lambda` new solutions and adds them to the population. The `(1+1)-EA` with `br=0` probability to use the binary operator is equivalent to [RLS](https://thomasweise.github.io/moptipy/moptipy.algorithms.html#moptipy.algorithms.rls.RLS).
677+
5. [General EA](https://thomasweise.github.io/moptipy/moptipy.algorithms.so.html#moptipy.algorithms.so.general_ea.GeneralEA) a generalized version of the (mu+lambda)-EA that can additionally be configured with a [fitness assignment process](https://thomasweise.github.io/moptipy/moptipy.algorithms.so.fitnesses.html) and both survival and mating [selection algorithms](https://thomasweise.github.io/moptipy/moptipy.algorithms.modules.html#module-moptipy.algorithms.modules.selection).
677678

678679

679680
#### 4.1.2. Multi-Objective Optimization
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"""This package provides a set of re-useable algorithm modules."""
1+
"""This package provides a set of reuseable algorithm modules."""

moptipy/algorithms/modules/selection.py

+50-16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,40 @@
1-
"""The base class for selection algorithms."""
1+
"""
2+
Selection algorithms are common modules that choose `n` out of `N` objects.
3+
4+
:class:`~moptipy.algorithms.modules.selection.Selection` is especially
5+
important in Evolutionary Algorithms
6+
(`~moptipy.algorithms.so.general_ea.GeneralEA`), where it is used in two
7+
places: As *survival selection*, it chooses which points will be allowed to
8+
remain in the population and, hence, survive into the mating pool for the next
9+
generation. As *mating selection* methods, they choose the inputs of the
10+
search operations from the mating pool.
11+
12+
:class:`~moptipy.algorithms.modules.selection.Selection` algorithms must
13+
*only* use the
14+
:attr:`~moptipy.algorithms.modules.selection.FitnessRecord.fitness` of a
15+
solution record (and random numbers) to make their decisions. These fitness
16+
values are subject to minimization. They can equal to the objective values in
17+
optimization or stem from a :class:`~moptipy.algorithms.so.fitness.Fitness`
18+
Assignment Process.
19+
20+
The following selection algorithms have currently been implemented:
21+
22+
- :class:`~moptipy.algorithms.modules.selections.best.Best` selection
23+
selects the best `n` solutions without replacement. This is a common
24+
strategy for survival selection, especially in (mu+lambda) EAs
25+
(compatible to :class:`~moptipy.algorithms.so.ea.EA`).
26+
- :class:`~moptipy.algorithms.modules.selections.tournament.Tournament`
27+
selection conducts a tournament with `k` contestants for of the `n` slots
28+
in the destination and the winners of the tournaments are chosen.
29+
- :class:`~moptipy.algorithms.modules.selections.random_without_replacement\
30+
.RandomWithoutReplacement` selects random solutions without replacement. It is
31+
a common strategy for mating selection.
32+
- :class:`~moptipy.algorithms.modules.selections.fitness_proportionate_sus\
33+
.FitnessProportionateSUS` performs fitness proportionate selection for
34+
minimization using stochastic uniform sampling and, optionally, a minimum
35+
selection probability threshold. It is the classic survival selection
36+
algorithm in Genetic Algorithm.
37+
"""
238
from typing import List, Protocol, Union, Callable, Any
339

440
from numpy.random import Generator
@@ -8,18 +44,15 @@
844

945

1046
class FitnessRecord(Protocol):
11-
"""
12-
A fitness record stores data together with a fitness.
13-
14-
The fitness should then be the only criterion used for selection.
15-
"""
47+
"""A fitness record stores data together with a fitness."""
1648

17-
#: the fitness value
49+
#: the fitness value, the only criterion to be used by a selection
50+
#: algorithm
1851
fitness: Union[int, float]
1952

2053
def __lt__(self, other) -> bool:
2154
"""
22-
Compare this fitness record with another fitness record.
55+
Compare the fitness of this record with the fitness of another one.
2356
2457
:param other: the other fitness record
2558
"""
@@ -33,18 +66,19 @@ def select(self, source: List[FitnessRecord],
3366
dest: Callable[[FitnessRecord], Any],
3467
n: int, random: Generator) -> None: # pylint: disable=W0613
3568
"""
36-
Select `n` records from `source` and append them to `dest`.
69+
Select `n` records from `source` and pass them to `dest`.
3770
3871
When choosing the `n` records from `source` to be appended to `dest`,
39-
only the :attr:`~FitnessRecord.fitness` attribute of the records and
40-
the random numbers from `random` should be used as decision criteria.
72+
only the :attr:`~FitnessRecord.fitness` attribute of the records (and
73+
the random numbers from `random`) must be used as decision criteria.
4174
4275
Selection algorithms are modules of the fully-configurable
43-
Evolutionary Algorithm :class:`~moptipy.algorithms.so.full_ea.FullEA`.
44-
They can utilize fitness values computed by the fitness assignment
45-
processes (:class:`~moptipy.algorithms.so.fitness.Fitness`). Of
46-
course, they can also be applied in different contexts and are not
47-
bound to single-objective optimization.
76+
Evolutionary Algorithm
77+
:class:`~moptipy.algorithms.so.general_ea.GeneralEA`. They can utilize
78+
fitness values computed by the fitness assignment processes
79+
(:class:`~moptipy.algorithms.so.fitness.Fitness`). Of course, they can
80+
also be applied in different contexts and are not bound to
81+
single-objective optimization.
4882
4983
:param source: the list with the records to select from
5084
:param dest: the destination collector to invoke for each selected
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
1-
"""The set of selections algorithms."""
1+
"""
2+
The set of selection algorithms.
3+
4+
:class:`~moptipy.algorithms.modules.selection.Selection` algorithms are
5+
algorithms that choose elements from a pool of records based on their
6+
:attr:`~moptipy.algorithms.modules.selection.FitnessRecord.fitness` and
7+
random numbers. They are commonly used in Evolutionary Algorithms
8+
(:class:`~moptipy.algorithms.so.general_ea.GeneralEA`).
9+
"""

moptipy/algorithms/so/fitness.py

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,38 @@
1-
"""The base class for fitness assignment processes."""
1+
"""
2+
Fitness Assignment Processes assign scalar fitnesses to solutions.
3+
4+
A :class:`~moptipy.algorithms.so.fitness.Fitness` Assignment Process uses
5+
the information of a set of instances of
6+
:class:`~moptipy.algorithms.so.fitness.FRecord` to compute their scalar
7+
:attr:`~moptipy.algorithms.modules.selection.FitnessRecord.fitness`.
8+
This fitness is then used by
9+
:class:`~moptipy.algorithms.modules.selection.Selection` algorithms.
10+
:class:`~moptipy.algorithms.modules.selection.Selection` is important in,
11+
e.g., Evolutionary Algorithms (`~moptipy.algorithms.so.general_ea.GeneralEA`),
12+
where it is used in two places: As *survival selection*, it chooses which
13+
points will be allowed to remain in the population and, hence, survive into
14+
the mating pool for the next generation. As *mating selection* methods, they
15+
choose the inputs of the search operations from the mating pool.
16+
17+
The following :class:`~moptipy.algorithms.so.fitness.Fitness` Assignment
18+
Processes have been implemented so far:
19+
20+
- :class:`~moptipy.algorithms.so.fitnesses.direct.Direct` directly copies the
21+
objective values (:attr:`~moptipy.algorithms.so.record.Record.f`) of the
22+
solution records directly over to the fitness.
23+
- :class:`~moptipy.algorithms.so.fitnesses.rank.Rank` ranks the solutions by
24+
their objective values and uses the ranks as fitness.
25+
- :class:`~moptipy.algorithms.so.fitnesses.rank_and_iteration\
26+
.RankAndIteration` also uses the rank of the objective values in the fitness.
27+
Additionally, if two solutions have the same objective value but one of them
28+
is newer, then the newer one will receive the better fitness. This is done
29+
by accessing the iteration counter
30+
(:attr:`~moptipy.algorithms.so.record.Record.it`) of the solution records.
31+
- :class:`~moptipy.algorithms.so.fitnesses.ffa.FFA` performs the Frequency
32+
Fitness Assignment which is suitable for problems with few different
33+
objective values and large computational budgets.
34+
35+
"""
236

337
from math import inf
438
from typing import Union, List

moptipy/algorithms/so/fitnesses/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Fitness assignment processes.
33
44
Fitness assignment processes are modules of the fully-configurable
5-
Evolutionary Algorithm :class:`~moptipy.algorithms.so.full_ea.FullEA`.
5+
Evolutionary Algorithm :class:`~moptipy.algorithms.so.general_ea.GeneralEA`.
66
The transform objective values to scalar fitness values that are then used
77
by :class:`~moptipy.algorithms.modules.selection.Selection` algorithms.
88
"""

moptipy/algorithms/so/full_ea.py moptipy/algorithms/so/general_ea.py

+28-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
11
"""
2-
A fully configurable (mu+lambda) Evolutionary Algorithm.
2+
A fully configurable, general (mu+lambda) Evolutionary Algorithm.
3+
4+
This evolutionary algorithm begins by sampling
5+
:attr:`~moptipy.algorithms.so.ea.EA.mu`
6+
solutions using the nullary search operation
7+
:attr:`~moptipy.api.algorithm.Algorithm0.op0`. In each iteration, it then uses
8+
:attr:`~moptipy.algorithms.so.ea.EA.mu` existing solutions as input for
9+
the search operations, where, for each solution to be sampled, the binary
10+
operation :attr:`~moptipy.api.algorithm.Algorithm2.op2` is used with
11+
probability :attr:`~moptipy.algorithms.so.ea.EA.br` and (otherwise), the unary
12+
operator :attr:`~moptipy.api.algorithm.Algorithm1` is used. The inputs of both
13+
operators are chosen from the :attr:`~moptipy.algorithms.so.ea.EA.mu`
14+
solutions using :attr:`~moptipy.algorithms.so.general_ea.GeneralEA.mating`
15+
selection. After :attr:`~moptipy.algorithms.so.ea.EA.lambda_` new solutions
16+
have been created this way (and have been evaluated as well), a fitness
17+
assignment process (:class:`~moptipy.algorithms.so.fitness.Fitness`) assigns
18+
fitness values to them based on their objective values
19+
(:attr:`~moptipy.algorithms.so.record.Record.f`), maybe also using the index
20+
of the iteration (:attr:`~moptipy.algorithms.so.record.Record.it`) in which
21+
they were created. The survival selection
22+
:attr:`~moptipy.algorithms.so.general_ea.GeneralEA.survival` then chooses,
23+
from the joint set of `mu+lambda` solutions, the `mu` solutions for the
24+
next iteration. Both mating and survival selection are instances of class
25+
:class:`~moptipy.algorithms.modules.selection.Selection`.
326
427
This algorithm is equivalent to :class:`~moptipy.algorithms.so.ea.EA`, but
528
allows for using a customized fitness assignment step
629
(:class:`~moptipy.algorithms.so.fitness.Fitness`) as well as customizable
7-
survival and mating selection
8-
(:class:`~moptipy.algorithms.modules.selection.Selection`).
30+
survival and :attr:`~moptipy.algorithms.so.general_ea.GeneralEA.mating`
31+
selection (:class:`~moptipy.algorithms.modules.selection.Selection`).
932
1033
1. Thomas Bäck, David B. Fogel, and Zbigniew Michalewicz, eds., *Handbook of
1134
Evolutionary Computation.* 1997. Computational Intelligence Library.
@@ -56,7 +79,7 @@ def __init__(self, x, f: Union[int, float], selected: bool = False):
5679
self._selected: bool = selected
5780

5881

59-
class FullEA(EA):
82+
class GeneralEA(EA):
6083
"""The fully customizable (mu+lambda) EA."""
6184

6285
def solve(self, process: Process) -> None:
@@ -174,7 +197,7 @@ def __init__(self, op0: Op0,
174197
fitness: Optional[Fitness] = None,
175198
survival: Optional[Selection] = None,
176199
mating: Optional[Selection] = None,
177-
name: str = "fullea") -> None:
200+
name: str = "generalEa") -> None:
178201
"""
179202
Create the customizable Evolutionary Algorithm (EA).
180203

moptipy/examples/jssp/experiment.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from moptipy.algorithms.so.ea import EA
1515
from moptipy.algorithms.so.fitnesses.direct import Direct
1616
from moptipy.algorithms.so.fitnesses.rank import Rank
17-
from moptipy.algorithms.so.full_ea import FullEA
17+
from moptipy.algorithms.so.general_ea import GeneralEA
1818
from moptipy.algorithms.so.greedy_2plus1_ea_mod import GreedyTwoPlusOneEAmod
1919
from moptipy.algorithms.so.hill_climber import HillClimber
2020
from moptipy.algorithms.so.hill_climber_with_restarts import \
@@ -130,31 +130,31 @@
130130
for mu_lambda in [4, 32]:
131131
DEFAULT_ALGORITHMS.append(cast(
132132
Callable[[Instance, Permutations], Algorithm],
133-
lambda inst, pwr, ml=mu_lambda: FullEA(
133+
lambda inst, pwr, ml=mu_lambda: GeneralEA(
134134
Op0Shuffle(pwr), Op1Swap2(),
135135
Op2GeneralizedAlternatingPosition(pwr),
136136
ml, ml, 0.05,
137137
survival=Tournament(2)))
138138
)
139139
DEFAULT_ALGORITHMS.append(cast(
140140
Callable[[Instance, Permutations], Algorithm],
141-
lambda inst, pwr, ml=mu_lambda: FullEA(
141+
lambda inst, pwr, ml=mu_lambda: GeneralEA(
142142
Op0Shuffle(pwr), Op1Swap2(),
143143
Op2GeneralizedAlternatingPosition(pwr),
144144
ml, ml, 0.05,
145145
survival=Tournament(4)))
146146
)
147147
DEFAULT_ALGORITHMS.append(cast(
148148
Callable[[Instance, Permutations], Algorithm],
149-
lambda inst, pwr, ml=mu_lambda: FullEA(
149+
lambda inst, pwr, ml=mu_lambda: GeneralEA(
150150
Op0Shuffle(pwr), Op1Swap2(),
151151
Op2GeneralizedAlternatingPosition(pwr),
152152
ml, ml, 0.05,
153153
mating=Tournament(2)))
154154
)
155155
DEFAULT_ALGORITHMS.append(cast(
156156
Callable[[Instance, Permutations], Algorithm],
157-
lambda inst, pwr, ml=mu_lambda: FullEA(
157+
lambda inst, pwr, ml=mu_lambda: GeneralEA(
158158
Op0Shuffle(pwr), Op1Swap2(),
159159
Op2GeneralizedAlternatingPosition(pwr),
160160
ml, ml, 0.05,
@@ -163,21 +163,21 @@
163163
)
164164
DEFAULT_ALGORITHMS.append(cast(
165165
Callable[[Instance, Permutations], Algorithm],
166-
lambda inst, pwr, ml=mu_lambda: FullEA(
166+
lambda inst, pwr, ml=mu_lambda: GeneralEA(
167167
Op0Shuffle(pwr), Op1Swap2(),
168168
Op2GeneralizedAlternatingPosition(pwr),
169169
ml, ml, 0.05,
170170
fitness=Direct(),
171-
survival=FitnessProportionateSUS(1 / (ml ** 2))))
171+
survival=FitnessProportionateSUS(0.01)))
172172
)
173173
DEFAULT_ALGORITHMS.append(cast(
174174
Callable[[Instance, Permutations], Algorithm],
175-
lambda inst, pwr, ml=mu_lambda: FullEA(
175+
lambda inst, pwr, ml=mu_lambda: GeneralEA(
176176
Op0Shuffle(pwr), Op1Swap2(),
177177
Op2GeneralizedAlternatingPosition(pwr),
178178
ml, ml, 0.05,
179179
fitness=Rank(),
180-
survival=FitnessProportionateSUS(1 / (ml ** 2))))
180+
survival=FitnessProportionateSUS(0.01)))
181181
)
182182

183183

tests/algorithms/so/test_algorithm_equivalences.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from moptipy.algorithms.so.ea import EA
1010
from moptipy.algorithms.so.fea1plus1 import FEA1plus1
1111
from moptipy.algorithms.so.fitnesses.ffa import FFA
12-
from moptipy.algorithms.so.full_ea import FullEA
12+
from moptipy.algorithms.so.general_ea import GeneralEA
1313
from moptipy.algorithms.so.record import Record
1414
from moptipy.algorithms.so.rls import RLS
1515
from moptipy.api.operators import Op0, Op1, Op2
@@ -29,7 +29,7 @@ def __test_opoea_equals_rls():
2929
verify_algorithms_equivalent([
3030
lambda bs, f: RLS(op0, op1),
3131
lambda bs, f: EA(op0, op1, op2, 1, 1, 0.0),
32-
lambda bs, f: FullEA(op0, op1, op2, 1, 1, 0.0),
32+
lambda bs, f: GeneralEA(op0, op1, op2, 1, 1, 0.0),
3333
])
3434

3535

@@ -128,7 +128,7 @@ def test_fitness_ea_equals_ea():
128128

129129
verify_algorithms_equivalent([
130130
lambda bs, f: __EAC(op0, op1, op2, mu, lambda_, br),
131-
lambda bs, f: FullEA(op0, op1, op2, mu, lambda_, br),
131+
lambda bs, f: GeneralEA(op0, op1, op2, mu, lambda_, br),
132132
])
133133

134134

@@ -139,5 +139,5 @@ def test_fitness_ea_with_ffa_equals_fea():
139139

140140
verify_algorithms_equivalent([
141141
lambda bs, f: FEA1plus1(op0, op1),
142-
lambda bs, f: FullEA(op0, op1, fitness=FFA(f)),
142+
lambda bs, f: GeneralEA(op0, op1, fitness=FFA(f)),
143143
])

0 commit comments

Comments
 (0)