Source code for moptipy.algorithms.so.fitnesses.rank

"""
A fitness representing the rank of a solution based on its quality.

First, all solutions are sorted based on their objective values. Then, they
receive their rank, i.e., index+1 in the sorted list, as fitness. If two
elements have the same objective value, they also receive the same rank.
This rank will be the index+1 of the first element.

>>> # The first value in FRecord(x, y) is the solution, which is not matter
>>> # here. The second value is the objective value used for ranking.
>>> l = [FRecord(1, 10), FRecord(2, 3), FRecord(3, 3), FRecord(4, 2)]
>>> from numpy.random import default_rng
>>> Rank().assign_fitness(l, default_rng())
>>> for z in l:
...     print(f"x={z.x}, fitness={z.fitness}")
x=4, fitness=1
x=2, fitness=2
x=3, fitness=2
x=1, fitness=4

Together with fitness proportionate selection (:class:`~moptipy.algorithms.\
modules.selections.fitness_proportionate_sus.FitnessProportionateSUS`), it
works very similar to linear ranking selection in an EA
(:class:`~moptipy.algorithms.so.general_ea.GeneralEA`).

1. L. Darrell Whitley. The GENITOR Algorithm and Selection Pressure: Why
   Rank-Based Allocation of Reproductive Trials is Best. In J. David Schaffer,
   ed., Proceedings of the 3rd International Conference on Genetic Algorithms
   (ICGA'89), June 4-7, 1989, Fairfax, VA, USA, pages 116-121. San Francisco,
   CA, USA: Morgan Kaufmann Publishers Inc. ISBN: 1-55860-066-3
   https://www.researchgate.net/publication/2527551
2. Tobias Blickle and Lothar Thiele. A Comparison of Selection Schemes used in
   Genetic Algorithms. Second edition, December 1995. TIK-Report 11 from the
   Eidgenössische Technische Hochschule (ETH) Zürich, Department of Electrical
   Engineering, Computer Engineering and Networks Laboratory (TIK), Zürich,
   Switzerland. ftp://ftp.tik.ee.ethz.ch/pub/publications/TIK-Report11.ps
"""

from math import inf

from numpy.random import Generator

from moptipy.algorithms.so.fitness import Fitness, FRecord


# start book
[docs] class Rank(Fitness): """A fitness computing the rank of an individual based on its quality."""
[docs] def assign_fitness(self, p: list[FRecord], random: Generator) -> None: """ Assign the rank as fitness. :param p: the list of records :param random: ignored >>> l = [FRecord(0, 10), FRecord(1, 5), FRecord(2, 5), FRecord(3, -1)] >>> from numpy.random import default_rng >>> Rank().assign_fitness(l, default_rng()) >>> l[0].x 3 >>> l[0].fitness 1 >>> l[1].x 1 >>> l[1].fitness 2 >>> l[2].x 2 >>> l[2].fitness 2 >>> l[3].x 0 >>> l[3].fitness 4 """ for rec in p: # first copy rec.f to rec.fitness rec.fitness = rec.f # because then we can easily sort p.sort() # sort based on objective values rank: int = -1 # the variable for storing the current rank last_f: int | float = -inf # the previous objective value for i, rec in enumerate(p): # iterate over list v = rec.fitness # get the current objective value if v > last_f: # only increase rank if objective f changes rank = i + 1 # +1 so smallest-possible fitness is 1 last_f = v # remember objective value for comparison rec.fitness = rank # assign the rank (same f = same rank)
# end book def __str__(self): """ Get the name of this rank-based fitness assignment. :return: the name of this fitness assignment strategy :retval "rank": always """ return "rank"