Coverage for /builds/debichem-team/python-ase/ase/ga/convergence.py: 26.42%

53 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-03-06 04:00 +0000

1"""Classes that determine convergence of an algorithm run 

2based on population stagnation or max raw score reached""" 

3from ase.ga import get_raw_score 

4 

5 

6class Convergence: 

7 """ 

8 Base class for all convergence object to be based on. 

9 It is necessary to supply the population instance, to be 

10 able to obtain current and former populations. 

11 """ 

12 

13 def __init__(self, population_instance): 

14 self.pop = population_instance 

15 self.pops = {} 

16 

17 def converged(self): 

18 """This function is called to find out if the algorithm 

19 run has converged, it should return True or False. 

20 Overwrite this in the inherited class.""" 

21 raise NotImplementedError 

22 

23 def populate_pops(self, to_gen): 

24 """Populate the pops dictionary with how the population 

25 looked after i number of generations.""" 

26 for i in range(to_gen): 

27 if i not in self.pops.keys(): 

28 self.pops[i] = self.pop.get_population_after_generation(i) 

29 

30 

31class GenerationRepetitionConvergence(Convergence): 

32 """Returns True if the latest finished population is stagnated for 

33 number_of_generations. 

34 

35 Parameters: 

36 

37 number_of_generations: int 

38 How many generations need to be equal before convergence. 

39 

40 number_of_individuals: int 

41 How many of the fittest individuals should be included in the 

42 convergence test. Default is -1 meaning all in the population. 

43 

44 max_generations: int 

45 The maximum number of generations the GA is allowed to run. 

46 Default is indefinite. 

47 """ 

48 

49 def __init__(self, population_instance, number_of_generations, 

50 number_of_individuals=-1, max_generations=100000000): 

51 Convergence.__init__(self, population_instance) 

52 self.numgens = number_of_generations 

53 self.numindis = number_of_individuals 

54 self.maxgen = max_generations 

55 

56 def converged(self): 

57 size = self.pop.pop_size 

58 cur_gen_num = self.pop.dc.get_generation_number(size) 

59 

60 if cur_gen_num >= self.maxgen: 

61 return True 

62 

63 if cur_gen_num <= 1: 

64 return False 

65 

66 cur_pop = self.pop.get_current_population() 

67 newest = max( 

68 i.info['key_value_pairs']['generation'] 

69 for i in cur_pop[: self.numindis] 

70 ) 

71 if newest + self.numgens > cur_gen_num: 

72 return False 

73 

74 self.populate_pops(cur_gen_num) 

75 

76 duplicate_gens = 1 

77 latest_pop = self.pops[cur_gen_num - 1] 

78 for i in range(cur_gen_num - 2, -1, -1): 

79 test_pop = self.pops[i] 

80 if test_pop[:self.numindis] == latest_pop[:self.numindis]: 

81 duplicate_gens += 1 

82 if duplicate_gens >= self.numgens: 

83 return True 

84 return False 

85 

86 

87class RawScoreConvergence(Convergence): 

88 """Returns True if the supplied max_raw_score has been reached""" 

89 

90 def __init__(self, population_instance, max_raw_score, eps=1e-3): 

91 Convergence.__init__(self, population_instance) 

92 self.max_raw_score = max_raw_score 

93 self.eps = eps 

94 

95 def converged(self): 

96 cur_pop = self.pop.get_current_population() 

97 if abs(get_raw_score(cur_pop[0]) - self.max_raw_score) <= self.eps: 

98 return True 

99 return False 

100 

101 

102class NeverConvergence: 

103 """Test class that never converges.""" 

104 

105 def __init__(self): 

106 pass 

107 

108 def converged(self): 

109 return False