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

1""" 

2Fitness Assignment Processes assign scalar fitnesses to solutions. 

3 

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. 

16 

17The following :class:`~moptipy.algorithms.so.fitness.Fitness` Assignment 

18Processes 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.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. 

34 

35""" 

36 

37from math import inf 

38 

39from numpy.random import Generator 

40from pycommons.types import type_error 

41 

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 

46 

47 

48# start book 

49class FRecord(Record, FitnessRecord): 

50 """A point `x` in the search space with its quality and fitness.""" 

51 

52# end book 

53 

54 def __init__(self, x, f: int | float): 

55 """ 

56 Create the record. 

57 

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 

64 

65 def __lt__(self, other) -> bool: 

66 """ 

67 Precedence on better ftness. 

68 

69 :param other: the other record 

70 :returns: `True` if this record has a better fitness value 

71 (:attr:`fitness`) 

72 

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 

94 

95 

96# start book 

97class Fitness(Component): 

98 """The base class for fitness assignment processes.""" 

99 

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`. 

104 

105 :param p: the list of :class:`FRecord` instances 

106 :param random: the random number generator 

107 """ 

108# end book 

109 

110 def log_information_after_run(self, process: Process) -> None: 

111 """ 

112 Log the information of this fitness assignment process to the process. 

113 

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 """ 

121 

122 

123def check_fitness(fitness: Fitness) -> Fitness: 

124 """ 

125 Check whether an object is a valid instance of :class:`Fitness`. 

126 

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