Coverage for moptipy / algorithms / so / fitnesses / rank_and_iteration.py: 100%

25 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-24 08:49 +0000

1""" 

2A fitness combining the rank of a solution with the iteration of its creation. 

3 

4This fitness assignment process is compatible with the simple (mu+lambda) 

5Evolutionary Algorithm implemented in :class:`~moptipy.algorithms.so.ea.EA`. 

6It will assign a better fitness to a solution which has a better objective 

7value. Ties will be broken based on the iteration counter 

8:attr:`~moptipy.algorithms.so.record.Record.it` of the solution records 

9:class:`~moptipy.algorithms.so.record.Record`. 

10""" 

11 

12from math import inf 

13from typing import Final 

14 

15from numpy.random import Generator 

16 

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

18 

19 

20# start book 

21class RankAndIteration(Fitness): 

22 """ 

23 A fitness joining objective rank and creation iteration. 

24 

25 The fitness assignment strategy will use two pieces of information to 

26 determine the fitness of a solution: 

27 

28 1. the rank the solutions by their objective values 

29 (:attr:`~moptipy.algorithms.so.record.Record.f`). If two solutions have 

30 the same fitness, they get the same rank, but the next-worst solution 

31 will then get a rank with is larger by 2. 

32 2. the iteration index (:attr:`~moptipy.algorithms.so.record.Record.it`) 

33 relative to the maximum and minimum iteration index. 

34 

35 It will multiply the rank of the solution with the range of the iteration 

36 index in the population and then add the maximum iteration index minus the 

37 iteration index of the solution, i.e., 

38 

39 `fitness(x) = rank(f(x)) * (max_it - min_it + 1) + (max_it - it(x))` 

40 

41 This way, better solutions receive better fitness and ties are broken such 

42 that younger solutions (with higher iteration index) are preferred. 

43 In combination with best selection 

44 (:class:`moptipy.algorithms.modules.selections.best.Best`), 

45 this replicates the behavior of our simple (mu+lambda) Evolutionary 

46 Algorithm (:class:`~moptipy.algorithms.so.ea.EA`). 

47 """ 

48 

49 def assign_fitness(self, p: list[FRecord], random: Generator) -> None: 

50 """ 

51 Assign the rank and iteration fitness. 

52 

53 :param p: the list of records 

54 :param random: ignored 

55 """ 

56 min_it: int = 9_223_372_036_854_775_808 # minimum iteration index 

57 max_it: int = -1 # the maximum iteration index 

58 

59 # In the first iteration, we assign objective value as fitness 

60 # (for sorting) and get the bounds of the iteration indices. 

61 for rec in p: # iterate over list p 

62 rec.fitness = rec.f # set f as fitness for sorting 

63 it: int = rec.it # get iteration index from record 

64 min_it = min(min_it, it) 

65 max_it = max(max_it, it) 

66 p.sort() # sort based on objective values 

67 

68 it_range: Final[int] = max_it - min_it + 1 # range of it index 

69 rank: int = -1 # the variable for storing the current rank 

70 last_f: int | float = -inf # the previous objective value 

71 for i, rec in enumerate(p): # iterate over list 

72 v = rec.fitness # get the current objective value 

73 if v > last_f: # only increase rank if objective f changes 

74 rank = i + 1 # +1 so smallest-possible fitness is 1 

75 last_f = v # remember objective value for comparison 

76 rec.fitness = (rank * it_range) + max_it - rec.it 

77# end book 

78 

79 def __str__(self): 

80 """ 

81 Get the name of this fitness assignment. 

82 

83 :return: the name of this fitness assignment strategy 

84 :retval "rankAndIt": always 

85 """ 

86 return "rankAndIt"