Coverage for moptipy / algorithms / so / fitness.py: 91%
22 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
1"""
2Fitness Assignment Processes assign scalar fitnesses to solutions.
4A :class:`~moptipy.algorithms.so.fitness.Fitness` Assignment Process uses
5the 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`.
8This fitness is then used by
9:class:`~moptipy.algorithms.modules.selection.Selection` algorithms.
10:class:`~moptipy.algorithms.modules.selection.Selection` is important in,
11e.g., Evolutionary Algorithms (`~moptipy.algorithms.so.general_ea.GeneralEA`),
12where it is used in two places: As *survival selection*, it chooses which
13points will be allowed to remain in the population and, hence, survive into
14the mating pool for the next generation. As *mating selection* methods, they
15choose the inputs of the search operations from the mating pool.
17The following :class:`~moptipy.algorithms.so.fitness.Fitness` Assignment
18Processes have been implemented so far:
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.ffa.ffa_fitness.FFA` performs the Frequency
32 Fitness Assignment which is suitable for problems with few different
33 objective values and large computational budgets.
35"""
37from math import inf
39from numpy.random import Generator
40from pycommons.types import type_error
42from moptipy.algorithms.modules.selection import FitnessRecord
43from moptipy.algorithms.so.record import Record
44from moptipy.api.component import Component
45from moptipy.api.process import Process
48# start book
49class FRecord(Record, FitnessRecord):
50 """A point `x` in the search space with its quality and fitness."""
52# end book
54 def __init__(self, x, f: int | float):
55 """
56 Create the record.
58 :param x: the data structure for a point in the search space
59 :param f: the corresponding objective value
60 """
61 super().__init__(x, f)
62 #: the fitness assigned to the solution `x`
63 self.fitness: int | float = inf
65 def __lt__(self, other) -> bool:
66 """
67 Precedence on better ftness.
69 :param other: the other record
70 :returns: `True` if this record has a better fitness value
71 (:attr:`fitness`)
73 >>> r1 = FRecord(None, 1)
74 >>> r2 = FRecord(None, 1)
75 >>> r1 < r2
76 False
77 >>> r2 < r1
78 False
79 >>> r1.fitness = 10
80 >>> r2.fitness = 9
81 >>> r2 < r1
82 True
83 >>> r1 < r2
84 False
85 >>> r1.fitness = r2.fitness
86 >>> r1.it = 10
87 >>> r2.it = 9
88 >>> r1 < r2
89 False
90 >>> r2 < r1
91 False
92 """
93 return self.fitness < other.fitness
96# start book
97class Fitness(Component):
98 """The base class for fitness assignment processes."""
100 def assign_fitness(self, p: list[FRecord],
101 random: Generator) -> None: # pylint: disable=W0613
102 """
103 Assign a fitness to each element in the list `p`.
105 :param p: the list of :class:`FRecord` instances
106 :param random: the random number generator
107 """
108# end book
110 def log_information_after_run(self, process: Process) -> None:
111 """
112 Log the information of this fitness assignment process to the process.
114 An instance of :class:`~moptipy.api.process.Process` is given to this
115 method after the algorithm has completed its work. The fitness
116 assignment process then may store some data as a separate log section
117 via :meth:`~moptipy.api.process.Process.add_log_section` if it wants
118 to. Implementing this method is optional. This method is only invoked
119 if :meth:`~moptipy.api.process.Process.has_log` returns `True`.
120 """
123def check_fitness(fitness: Fitness) -> Fitness:
124 """
125 Check whether an object is a valid instance of :class:`Fitness`.
127 :param fitness: the Fitness object
128 :return: the object
129 :raises TypeError: if `fitness` is not an instance of :class:`Fitness`
130 """
131 if not isinstance(fitness, Fitness):
132 raise type_error(fitness, "op0", Fitness)
133 if fitness.__class__ is Fitness:
134 raise TypeError("cannot use abstract class 'Fitness' directly")
135 return fitness